From 7b9fc7d5558f2f88dcd1d9a29f247cdb25e484db Mon Sep 17 00:00:00 2001 From: Mario Vega Date: Wed, 21 Jan 2026 21:38:51 +0100 Subject: [PATCH 01/21] feat(ci): Create Devnet Workflows (#2053) * feat: Create devnet branch, rebase eip branch * fixes * fixes * fix * fixes * remove types * Update .github/actions/merge-eip-branches/action.yaml Co-authored-by: spencer * Update .github/actions/rebase-eip-branch/action.yaml Co-authored-by: spencer * Update .github/actions/merge-eip-branches/action.yaml Co-authored-by: spencer * PR review feedback * REMOTE env variable * Nit: Change names * fix --------- Co-authored-by: spencer --- .../actions/merge-eip-branches/action.yaml | 127 ++++++++++++++++++ .github/actions/rebase-eip-branch/action.yaml | 71 ++++++++++ .github/workflows/eip-rebase.yaml | 36 +++++ .github/workflows/update-devnet-branch.yaml | 44 ++++++ 4 files changed, 278 insertions(+) create mode 100644 .github/actions/merge-eip-branches/action.yaml create mode 100644 .github/actions/rebase-eip-branch/action.yaml create mode 100644 .github/workflows/eip-rebase.yaml create mode 100644 .github/workflows/update-devnet-branch.yaml diff --git a/.github/actions/merge-eip-branches/action.yaml b/.github/actions/merge-eip-branches/action.yaml new file mode 100644 index 00000000000..1a0b9afb640 --- /dev/null +++ b/.github/actions/merge-eip-branches/action.yaml @@ -0,0 +1,127 @@ +name: Merge EIP Branches +description: Merges multiple EIP branches into a devnet branch, by pushing using --force-with-lease + +inputs: + fork: + description: 'Fork name (e.g., amsterdam)' + required: true + devnet_name: + description: 'Devnet name (e.g., amsterdam/1, bal/2, eip-1234/3). Created branch will be `devnets/$devnet_name`' + required: true + eip_numbers: + description: 'Comma-separated list of EIP numbers (e.g., 1234,2345,3456)' + required: true + +runs: + using: "composite" + steps: + - name: Configure Git + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Merge EIP branches + shell: bash + env: + FORK: ${{ inputs.fork }} + DEVNET_NAME: ${{ inputs.devnet_name }} + EIP_NUMBERS: ${{ inputs.eip_numbers }} + REMOTE: origin + run: | + set -euo pipefail + + echo "FORK=$FORK" + echo "DEVNET_NAME=$DEVNET_NAME" + echo "EIP_NUMBERS=$EIP_NUMBERS" + + FORK_BRANCH="forks/${FORK}" + DEVNET_BRANCH="devnets/${DEVNET_NAME}" + + # Basic ref-format validation for user-provided pieces + # (git check-ref-format expects full refnames for some checks; --branch is easiest) + git check-ref-format --branch "${DEVNET_BRANCH}" >/dev/null + + # Convert comma-separated list to array + IFS=',' read -ra RAW_EIPS <<< "$EIP_NUMBERS" + + # Normalize: trim whitespace, drop empties + EIPS=() + for raw in "${RAW_EIPS[@]}"; do + eip="$(echo "$raw" | xargs)" # trims leading/trailing whitespace + if [[ -n "$eip" ]]; then + # Optional: ensure it looks numeric + if [[ ! "$eip" =~ ^[0-9]+$ ]]; then + echo "Error: Invalid EIP number ${eip}" + exit 1 + fi + EIPS+=("$eip") + fi + done + + if [[ ${#EIPS[@]} -eq 0 ]]; then + echo "Error: No EIP numbers provided after parsing '${EIP_NUMBERS}'" + exit 1 + fi + + git fetch "${REMOTE}" --prune + + # Build the list of expected remote branches and verify all exist, + # then checkout each branch and rebase it onto the fork branch automatically. + for eip in "${EIPS[@]}"; do + EIP_BRANCH="eips/${FORK}/eip-${eip}" + # Validate ref format for each constructed branch name + git check-ref-format --branch "${EIP_BRANCH}" >/dev/null || { + echo "Error: Computed branch name '${EIP_BRANCH}' is not a valid ref" + exit 1 + } + + if ! git show-ref --verify --quiet "refs/remotes/${REMOTE}/${EIP_BRANCH}"; then + echo "Error: Missing remote branch ${REMOTE}/${EIP_BRANCH}" + exit 1 + fi + + git checkout -B "${EIP_BRANCH}" "${REMOTE}/${EIP_BRANCH}" + + if git merge-base --is-ancestor "${REMOTE}/${FORK_BRANCH}" "${EIP_BRANCH}"; then + echo "${EIP_BRANCH} is already based on ${FORK_BRANCH}, skipping rebase" + else + if ! git rebase "${REMOTE}/${FORK_BRANCH}"; then + echo "Error: Automatic rebase of ${EIP_BRANCH} onto ${FORK_BRANCH} failed" + git rebase --abort || true + exit 1 + else + echo "Rebase of ${EIP_BRANCH} onto ${FORK_BRANCH} successful" + fi + fi + done + + # Create (or reset) devnet branch from first EIP + FIRST_EIP="eips/${FORK}/eip-${EIPS[0]}" + echo "Creating branch ${DEVNET_BRANCH} from ${FIRST_EIP}" + git checkout -B "${DEVNET_BRANCH}" "${FIRST_EIP}" + + # Merge remaining EIPs + for ((i=1; i<${#EIPS[@]}; i++)); do + EIP_BRANCH="eips/${FORK}/eip-${EIPS[$i]}" + echo "Merging ${EIP_BRANCH}..." + + if ! git merge "${EIP_BRANCH}" -m "Merge ${EIP_BRANCH} into ${DEVNET_BRANCH}"; then + echo "Error: Merge conflict occurred while merging ${EIP_BRANCH}" + git merge --abort || true + exit 1 + fi + done + + echo "All EIP branches merged successfully" + + - name: Push devnet branch + shell: bash + env: + DEVNET_NAME: ${{ inputs.devnet_name }} + REMOTE: origin + run: | + DEVNET_BRANCH="devnets/${DEVNET_NAME}" + + echo "Force pushing ${DEVNET_BRANCH} to ${REMOTE}" + git push --force-with-lease "${REMOTE}" "${DEVNET_BRANCH}" diff --git a/.github/actions/rebase-eip-branch/action.yaml b/.github/actions/rebase-eip-branch/action.yaml new file mode 100644 index 00000000000..37bbec6946d --- /dev/null +++ b/.github/actions/rebase-eip-branch/action.yaml @@ -0,0 +1,71 @@ +name: Rebase EIP Branch +description: Rebases an EIP branch onto a fork branch +inputs: + fork: + description: 'Fork name (e.g., amsterdam)' + required: true + eip_number: + description: 'EIP number' + required: true +runs: + using: "composite" + steps: + - name: Configure Git + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Rebase EIP branch (create if missing) + shell: bash + env: + FORK: ${{ inputs.fork }} + EIP_NUMBER: ${{ inputs.eip_number }} + REMOTE: origin + run: | + set -euo pipefail + + echo "FORK=$FORK" + echo "EIP_NUMBER=$EIP_NUMBER" + EIP_BRANCH="eips/${FORK}/eip-${EIP_NUMBER}" + FORK_BRANCH="forks/${FORK}" + + git fetch "${REMOTE}" --prune + + # Sanity: fork base must exist + if ! git show-ref --verify --quiet "refs/remotes/${REMOTE}/${FORK_BRANCH}"; then + echo "Error: Base fork branch ${REMOTE}/${FORK_BRANCH} does not exist" + exit 1 + fi + + # Create local branch from remote if it exists, else create from fork base + if git show-ref --verify --quiet "refs/remotes/${REMOTE}/${EIP_BRANCH}"; then + echo "Checking out existing ${EIP_BRANCH} (tracking ${REMOTE})" + git checkout -B "${EIP_BRANCH}" "${REMOTE}/${EIP_BRANCH}" + else + echo "Branch ${EIP_BRANCH} does not exist on ${REMOTE}; creating from ${REMOTE}/${FORK_BRANCH}" + git checkout -B "${EIP_BRANCH}" "${REMOTE}/${FORK_BRANCH}" + + # First push creates the remote branch (no force needed) + git push -u "${REMOTE}" "${EIP_BRANCH}" + fi + + echo "Rebasing ${EIP_BRANCH} onto ${REMOTE}/${FORK_BRANCH}" + if ! git rebase "${REMOTE}/${FORK_BRANCH}"; then + echo "Error: Rebase conflict occurred while rebasing ${EIP_BRANCH} onto ${FORK_BRANCH}" + git rebase --abort || true + exit 1 + fi + + echo "Rebase successful" + + - name: Push rebased branch + shell: bash + env: + FORK: ${{ inputs.fork }} + EIP_NUMBER: ${{ inputs.eip_number }} + REMOTE: origin + run: | + EIP_BRANCH="eips/${FORK}/eip-${EIP_NUMBER}" + echo "Force pushing ${EIP_BRANCH} to ${REMOTE}" + git push --force-with-lease "${REMOTE}" "${EIP_BRANCH}" diff --git a/.github/workflows/eip-rebase.yaml b/.github/workflows/eip-rebase.yaml new file mode 100644 index 00000000000..01c486a25c4 --- /dev/null +++ b/.github/workflows/eip-rebase.yaml @@ -0,0 +1,36 @@ +name: EIP Rebase Onto Fork Branch + +on: + workflow_dispatch: + inputs: + fork: + description: 'Fork name (e.g., amsterdam)' + required: true + type: string + eip_number: + description: 'EIP number' + required: true + type: string + +concurrency: + group: ${{ github.workflow }}-${{ github.event.inputs.fork }}-${{ github.event.inputs.eip_number }} + cancel-in-progress: false + +permissions: + contents: write + +jobs: + rebase-eip: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Rebase EIP branch onto fork + uses: ./.github/actions/rebase-eip-branch + with: + fork: ${{ github.event.inputs.fork }} + eip_number: ${{ github.event.inputs.eip_number }} diff --git a/.github/workflows/update-devnet-branch.yaml b/.github/workflows/update-devnet-branch.yaml new file mode 100644 index 00000000000..73f2229252e --- /dev/null +++ b/.github/workflows/update-devnet-branch.yaml @@ -0,0 +1,44 @@ +name: Update Devnet Branch + +on: + workflow_dispatch: + inputs: + fork: + description: 'Fork name (e.g., amsterdam)' + required: true + type: string + default: 'amsterdam' + devnet_name: + description: 'Devnet name (e.g., amsterdam/1, bal/2, eip-1234/3)' + required: true + type: string + default: 'bal/2' + eip_numbers: + description: 'Comma-separated list of EIP numbers (e.g., 1234,2345,3456)' + required: true + type: string + default: '8024,7843,7708,7778' + +concurrency: + group: ${{ github.workflow }}-${{ github.event.inputs.fork || 'amsterdam' }}-${{ github.event.inputs.devnet_name || github.ref }} + cancel-in-progress: false + +permissions: + contents: write + +jobs: + update-devnet-branch: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Merge EIP branches into devnet + uses: ./.github/actions/merge-eip-branches + with: + fork: ${{ github.event.inputs.fork }} + devnet_name: ${{ github.event.inputs.devnet_name }} + eip_numbers: ${{ github.event.inputs.eip_numbers }} From f1ba2b1d7f8fd88d1f608e95c5d940527f38c4bd Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 21 Jan 2026 14:40:11 -0700 Subject: [PATCH 02/21] feat(test): port static context static tests to python (#1960) * feat(test): port static context static tests to python Port STATICCALL tests with zero and non-zero value to precompiles * feat(test): split into legacy test and high-level API test + parametrize - Inspired by comment: https://github.com/ethereum/execution-specs/pull/1960#discussion_r2656834142 - Split the unreadable bytecode test from the comment where we can parametrize with all precompiles as well as with call_value = [0 and nonzero]. * chore: parametrize CREATE2 from Constantinople (where introduced) * refactor: address comments from PR #1960 * feat(tests): Add BAL expectations for static call tests >=Amsterdam * refactor(test): Update test name, add to BAL test_cases.md * chore: convert to state_test --------- Co-authored-by: Mario Vega --- docs/CHANGELOG.md | 1 + .../pytest_commands/plugins/forks/forks.py | 52 +- .../test_cases.md | 4 + tests/byzantium/eip214_staticcall/__init__.py | 7 + tests/byzantium/eip214_staticcall/spec.py | 17 + .../eip214_staticcall/test_staticcall.py | 672 ++++++++++++++++++ ...ueToPrecompileFromCalledContractFiller.yml | 197 ----- ...ompileFromContractInitializationFiller.yml | 207 ------ ...ValueToPrecompileFromTransactionFiller.yml | 176 ----- ...ueToPrecompileFromCalledContractFiller.yml | 234 ------ ...ompileFromContractInitializationFiller.yml | 226 ------ ...ValueToPrecompileFromTransactionFiller.yml | 216 ------ ...StaticcallForPrecompilesIssue683Filler.yml | 47 -- 13 files changed, 752 insertions(+), 1304 deletions(-) create mode 100644 tests/byzantium/eip214_staticcall/__init__.py create mode 100644 tests/byzantium/eip214_staticcall/spec.py create mode 100644 tests/byzantium/eip214_staticcall/test_staticcall.py delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromCalledContractFiller.yml delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromContractInitializationFiller.yml delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromTransactionFiller.yml delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromCalledContractFiller.yml delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromContractInitializationFiller.yml delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromTransactionFiller.yml delete mode 100644 tests/static/state_tests/stStaticFlagEnabled/StaticcallForPrecompilesIssue683Filler.yml diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a703a9b4855..3f1e21902e1 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -38,6 +38,7 @@ Test fixtures for use by clients are available for each release on the [Github r - 🔀 Relabel `@pytest.mark.repricing` markers in benchmark tests to reflect configurations requested for gas repricing analysis ([#1971](https://github.com/ethereum/execution-specs/pull/1971)). - ✨ New EIP-7702 test cases added ([#1974](https://github.com/ethereum/execution-specs/pull/1974)). - ✨ Add missing benchmark configurations / opcode to benchmark tests for repricing analysis([#2006](https://github.com/ethereum/execution-specs/pull/2006)). +- ✨ Port STATICCALL to CALL tests with zero and non-zero value transfer from `tests/static`, extending coverage with `pytest.mark.with_all_precompiles` ([#1960](https://github.com/ethereum/execution-specs/pull/1960)). ## [v5.4.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v5.4.0) - 2025-12-07 diff --git a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py index faabc10500c..197e9f595da 100644 --- a/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py +++ b/packages/testing/src/execution_testing/cli/pytest_commands/plugins/forks/forks.py @@ -1123,6 +1123,39 @@ def pytest_generate_tests(metafunc: pytest.Metafunc) -> None: parametrize_fork(metafunc, pytest_params) +def get_param_level_min_valid_fork(metafunc: Metafunc) -> Fork | None: + """ + Extract the minimum valid fork from param-level valid_from markers. + + Returns the earliest fork from any valid_from marker inside pytest.param, + or None if no such markers exist. + """ + min_fork: Fork | None = None + + for marker in metafunc.definition.iter_markers("parametrize"): + if len(marker.args) < 2: + continue + + for value in marker.args[1]: + if not isinstance(value, ParameterSet) or not value.marks: + continue + + for mark in value.marks: + mark_obj = mark.mark if hasattr(mark, "mark") else mark + if mark_obj.name == "valid_from" and mark_obj.args: + fork_name = mark_obj.args[0] + try: + for fork in ALL_FORKS: + if fork.name() == fork_name: + if min_fork is None or fork < min_fork: + min_fork = fork + break + except (ValueError, InvalidForkError): + pass + + return min_fork + + def add_fork_covariant_parameters( metafunc: Metafunc, fork_parametrizers: List[ForkParametrizer] ) -> None: @@ -1130,7 +1163,24 @@ def add_fork_covariant_parameters( Iterate over the fork covariant descriptors and add their values to the test function. """ - # Process all covariant decorators uniformly + # Check if any covariant markers are present + has_covariant_markers = any( + list(metafunc.definition.iter_markers(cd.marker_name)) + for cd in fork_covariant_decorators + ) or any( + marker.name == "parametrize_by_fork" + for marker in metafunc.definition.iter_markers() + ) + + # Filter forks before any param-level valid_from to avoid covariant + # assertion errors + if has_covariant_markers: + param_min_fork = get_param_level_min_valid_fork(metafunc) + if param_min_fork: + fork_parametrizers[:] = [ + fp for fp in fork_parametrizers if fp.fork >= param_min_fork + ] + for covariant_descriptor in fork_covariant_decorators: if list( metafunc.definition.iter_markers(covariant_descriptor.marker_name) diff --git a/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md b/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md index 29d77ea46a0..54d53064fe2 100644 --- a/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md +++ b/tests/amsterdam/eip7928_block_level_access_lists/test_cases.md @@ -115,6 +115,10 @@ | `test_selfdestruct_created_in_same_tx_with_revert` | Ensure BAL tracks selfdestruct with revert correctly (pre-Amsterdam test with BAL) | Contract created and selfdestructed in same tx with nested revert | BAL **MUST** track storage reads and balance changes for selfdestruct even with reverts | ✅ Completed | | `test_value_transfer_gas_calculation` | Ensure BAL correctly tracks OOG scenarios for CALL/CALLCODE/DELEGATECALL/STATICCALL (pre-Amsterdam test with BAL) | Nested calls with precise gas limits to test OOG behavior. For CALL with OOG: target account is read. For CALLCODE/DELEGATECALL/STATICCALL with OOG: target account **NOT** read (OOG before state access) | For CALL: target in BAL even with OOG. For CALLCODE/DELEGATECALL/STATICCALL: target **NOT** in BAL when OOG (state access deferred until after gas check) | ✅ Completed | | `test_bal_call_with_value_in_static_context` | Ensure BAL does NOT include target when CALL with value fails in static context | `static_caller` uses `STATICCALL` to call `caller`. `caller` attempts `CALL(target, value=1)` which must fail due to static context. Target is an empty account. | BAL **MUST NOT** include target because static context check (`is_static && value > 0`) must happen BEFORE any account access or BAL tracking. BAL **MUST** include `static_caller` with `storage_changes` (STATICCALL succeeded), `caller` with empty changes. | ✅ Completed | +| `test_staticcall_reentrant_call_to_precompile` | Ensure BAL captures STATICCALL reentry with CALL to precompile | Contract STATICCALLs itself. On reentry (CALLVALUE=0), attempts CALL to precompile with parametrized value. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: target with `storage_changes` (slot 0=1), precompile with empty changes. call_value>0: target with `storage_reads` (slot 0), precompile **NOT** in BAL (reverted before accessed). | ✅ Completed | +| `test_staticcall_call_to_precompile` | Ensure BAL captures STATICCALL → CALL to precompile chain | Contract A STATICCALLs contract B. B attempts CALL to precompile. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: contract_a with markers, contract_b empty (STATICCALLed), precompile empty. call_value>0: contract_a with `storage_reads` for slot 1, precompile **NOT** in BAL. | ✅ Completed | +| `test_staticcall_nested_call_to_precompile` | Ensure BAL captures nested CALL → STATICCALL → CALL to precompile | Contract B CALLs A. A STATICCALLs C. C attempts CALL to precompile. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: all contracts with markers/empty, precompile empty. call_value>0: contract_a with `storage_reads` for slot 1, precompile **NOT** in BAL. | ✅ Completed | +| `test_staticcall_call_to_precompile_from_contract_init` | Ensure BAL captures STATICCALL to precompile during CREATE init | Contract A CREATEs contract. Init code STATICCALLs B which CALLs precompile. File: `tests/byzantium/eip214_staticcall/test_staticcall.py`. | call_value=0: contract_a with markers/nonce, created_contract with markers/nonce, contract_b empty, precompile empty. call_value>0: created_contract with `storage_reads` for slot 1, precompile **NOT** in BAL. | ✅ Completed | | `test_bal_4788_simple` | Ensure BAL captures beacon root storage writes during pre-execution system call | Block with 2 normal user transactions: Alice sends 10 wei to Charlie, Bob sends 10 wei to Charlie. At block start (pre-execution), `SYSTEM_ADDRESS` calls `BEACON_ROOTS_ADDRESS` to store parent beacon root | BAL **MUST** include at `block_access_index=0`: `BEACON_ROOTS_ADDRESS` with two `storage_changes` (timestamp slot and beacon root slot); `SYSTEM_ADDRESS` **MUST NOT** be included in BAL. At `block_access_index=1`: Alice with `nonce_changes`, Charlie with `balance_changes` (10 wei). At `block_access_index=2`: Bob with `nonce_changes`, Charlie with `balance_changes` (20 wei total). | ✅ Completed | | `test_bal_4788_empty_block` | Ensure BAL captures beacon root storage writes in empty block | Block with no transactions. At block start (pre-execution), `SYSTEM_ADDRESS` calls `BEACON_ROOTS_ADDRESS` to store parent beacon root | BAL **MUST** include at `block_access_index=0`: `BEACON_ROOTS_ADDRESS` with two `storage_changes` (timestamp slot and beacon root slot); `SYSTEM_ADDRESS` **MUST NOT** be included in BAL. No transaction-related BAL entries. | ✅ Completed | | `test_bal_4788_query` | Ensure BAL captures storage reads when querying beacon root (valid and invalid queries) with optional value transfer | Parameterized test: Block 1 stores beacon root at timestamp 12. Block 2 queries with three timestamp scenarios (valid=12, invalid non-zero=42, invalid zero=0) and value (0 or 100 wei). Valid query (timestamp=12): reads both timestamp and root slots, writes returned value. If value > 0, beacon root contract receives balance. Invalid query with non-zero timestamp (timestamp=42): reads only timestamp slot before reverting, query contract has implicit SLOAD recorded (SSTORE reverts), no value transferred. Invalid query with zero timestamp (timestamp=0): reverts immediately without any storage access, query contract has implicit SLOAD recorded, no value transferred. | Block 1 BAL: System call writes. Block 2 BAL **MUST** include at `block_access_index=0`: System call writes for block 2. Valid case (timestamp=12) at `block_access_index=1`: `BEACON_ROOTS_ADDRESS` with `storage_reads` [timestamp_slot, root_slot] and `balance_changes` if value > 0, query contract with `storage_changes`. Invalid non-zero case (timestamp=42) at `block_access_index=1`: `BEACON_ROOTS_ADDRESS` with `storage_reads` [timestamp_slot only] and NO `balance_changes` (reverted), query contract with `storage_reads` [0] and NO `storage_changes`. Invalid zero case (timestamp=0) at `block_access_index=1`: `BEACON_ROOTS_ADDRESS` with NO `storage_reads` (reverts before access) and NO `balance_changes`, query contract with `storage_reads` [0] and NO `storage_changes`. | ✅ Completed | diff --git a/tests/byzantium/eip214_staticcall/__init__.py b/tests/byzantium/eip214_staticcall/__init__.py new file mode 100644 index 00000000000..dd2092814ad --- /dev/null +++ b/tests/byzantium/eip214_staticcall/__init__.py @@ -0,0 +1,7 @@ +""" +Test cases for EIP-214: STATICCALL opcode. + +EIP-214 introduced the STATICCALL opcode which creates a read-only call +context. Any state-modifying operations (including CALL with non-zero +value) within a STATICCALL context will cause the call to fail. +""" diff --git a/tests/byzantium/eip214_staticcall/spec.py b/tests/byzantium/eip214_staticcall/spec.py new file mode 100644 index 00000000000..497a6c9f4dc --- /dev/null +++ b/tests/byzantium/eip214_staticcall/spec.py @@ -0,0 +1,17 @@ +"""Defines EIP-214 specification reference.""" + +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ReferenceSpec: + """Defines the reference spec version and git path.""" + + git_path: str + version: str + + +ref_spec_214 = ReferenceSpec( + git_path="EIPS/eip-214.md", + version="009d0e1ce76b2c171c34bacdb2f13d606c9918b0", +) diff --git a/tests/byzantium/eip214_staticcall/test_staticcall.py b/tests/byzantium/eip214_staticcall/test_staticcall.py new file mode 100644 index 00000000000..d3926bcf710 --- /dev/null +++ b/tests/byzantium/eip214_staticcall/test_staticcall.py @@ -0,0 +1,672 @@ +""" +Tests for EIP-214 STATICCALL opcode behavior. + +STATICCALL creates a read-only call context where state-modifying operations +are forbidden. This includes CALL with non-zero value to any address. +""" + +import pytest +from execution_testing import ( + Account, + Address, + Alloc, + BalAccountExpectation, + BalBalanceChange, + BalNonceChange, + BalStorageChange, + BalStorageSlot, + BlockAccessListExpectation, + Conditional, + Fork, + Op, + StateTestFiller, + Transaction, + compute_create_address, +) + +from .spec import ref_spec_214 + +REFERENCE_SPEC_GIT_PATH = ref_spec_214.git_path +REFERENCE_SPEC_VERSION = ref_spec_214.version + + +def bal_marker_storage_changes( + marker: int, staticcall_result: int +) -> list[BalStorageSlot]: + """ + Build BAL storage changes for the common pattern of marker slots. + + Most tests write to slots 0, 1, 2 where: + - slot 0: marker value + - slot 1: STATICCALL result (0 or 1) + - slot 2: marker value + """ + return [ + BalStorageSlot( + slot=0, + slot_changes=[ + BalStorageChange(block_access_index=1, post_value=marker) + ], + ), + BalStorageSlot( + slot=1, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=staticcall_result + ) + ], + ), + BalStorageSlot( + slot=2, + slot_changes=[ + BalStorageChange(block_access_index=1, post_value=marker) + ], + ), + ] + + +def bal_expectation_for_contract_with_markers( + marker: int, + staticcall_result: int, + balance_change: int | None = None, + initial_balance: int = 0, +) -> BalAccountExpectation: + """ + Build BAL expectation for a contract that writes marker storage slots. + + Args: + marker: The marker value written to slots 0 and 2 + staticcall_result: The value written to slot 1 (STATICCALL result) + balance_change: If provided, include a balance change + initial_balance: The initial balance (for computing post_balance) + + """ + return BalAccountExpectation( + storage_changes=bal_marker_storage_changes(marker, staticcall_result), + balance_changes=( + [ + BalBalanceChange( + block_access_index=1, + post_balance=initial_balance + balance_change, + ) + ] + if balance_change is not None + else [] + ), + ) + + +@pytest.mark.with_all_precompiles +@pytest.mark.parametrize( + "call_value", [0, 2], ids=["zero_value", "nonzero_value"] +) +@pytest.mark.ported_from( + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/" + "stStaticFlagEnabled/StaticcallForPrecompilesIssue683Filler.yml" +) +@pytest.mark.valid_from("Byzantium") +def test_staticcall_reentrant_call_to_precompile( + pre: Alloc, + state_test: StateTestFiller, + precompile: Address, + call_value: int, + fork: Fork, +) -> None: + """ + Test CALL to precompile inside STATICCALL with zero and non-zero value. + + Regression test for ethereum/tests#683. + Source: https://github.com/ethereum/execution-specs/pull/1960#discussion_r2656834142 + + A single contract STATICCALLs itself. On reentry (detected via CALLVALUE=0, + since STATICCALL doesn't forward value), it attempts CALL to a precompile. + + - call_value=0: CALL succeeds in static context → STATICCALL returns 1 + - call_value>0: CALL violates static context, reverts frame → STATICCALL + returns 0 + """ + alice = pre.fund_eoa() + + # Contract that STATICCALLs itself on reentry (CALLVALUE=0), + # attempts CALL to precompile + target_code = Conditional( + # CALLVALUE=0 indicates we're inside the STATICCALL (reentry) + condition=Op.ISZERO(Op.CALLVALUE), + # try CALL with parametrized value (fails if value > 0) + if_true=Op.CALL(address=precompile, value=call_value), + # STATICCALL to self, store result (0=fail, 1=success) + if_false=Op.SSTORE(0, Op.STATICCALL(address=Op.ADDRESS)), + ) + + target_balance = 1000 + target = pre.deploy_contract(code=target_code, balance=target_balance) + + tx_value = 100 + tx = Transaction( + sender=alice, + to=target, + gas_limit=1_000_000, + value=tx_value, + protected=True, + ) + + bal_expectation = None + if fork.header_bal_hash_required(): + # Target contract always receives tx value + target_balance_changes = [ + BalBalanceChange( + block_access_index=1, post_balance=target_balance + tx_value + ) + ] + + # call_value > 0: SSTORE(0, 0) is a read; call_value == 0: real change + account_expectations: dict[Address, BalAccountExpectation | None] = { + target: ( + BalAccountExpectation( + storage_changes=[ + BalStorageSlot( + slot=0, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=1 + ) + ], + ), + ], + balance_changes=target_balance_changes, + ) + if call_value == 0 + else BalAccountExpectation( + storage_reads=[0], + balance_changes=target_balance_changes, + ) + ), + } + + if call_value == 0: + account_expectations[precompile] = BalAccountExpectation.empty() + else: + account_expectations[precompile] = None # reverted before accessed + + bal_expectation = BlockAccessListExpectation( + account_expectations=account_expectations + ) + + state_test( + pre=pre, + tx=tx, + expected_block_access_list=bal_expectation, + post={ + target: Account( + balance=target_balance + tx_value, + storage={0: 1 if call_value == 0 else 0}, + ), + }, + ) + + +@pytest.mark.with_all_precompiles +@pytest.mark.parametrize( + "call_value", [0, 2], ids=["zero_value", "nonzero_value"] +) +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromTransactionFiller.yml", + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromTransactionFiller.yml", + ], +) +@pytest.mark.valid_from("Byzantium") +def test_staticcall_call_to_precompile( + pre: Alloc, + state_test: StateTestFiller, + precompile: Address, + call_value: int, + fork: Fork, +) -> None: + """ + Test CALL to precompile inside STATICCALL with zero and non-zero value. + + Contract A STATICCALLs contract B. Contract B attempts to CALL precompile. + With value = 0, this succeeds. With value > 0, this fails (static context). + """ + alice = pre.fund_eoa() + + initial_contract_balance = 1000 + marker = 0xFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEED + + # Contract B: attempts CALL to precompile with the parametrized value + contract_b = pre.deploy_contract( + code=Op.CALL(gas=100_000, address=precompile, value=call_value), + balance=initial_contract_balance, + ) + + # Contract A: STATICCALLs contract B and stores the result + contract_a = pre.deploy_contract( + code=( + Op.SSTORE(0, marker) + + Op.SSTORE(1, Op.STATICCALL(gas=200_000, address=contract_b)) + + Op.SSTORE(2, marker) + ), + balance=initial_contract_balance, + ) + + tx_value = 100 + staticcall_result = 1 if call_value == 0 else 0 + + bal_expectation = None + if fork.header_bal_hash_required(): + contract_a_balance_changes = [ + BalBalanceChange( + block_access_index=1, + post_balance=initial_contract_balance + tx_value, + ) + ] + + # slot 1 read when call_value > 0 + account_expectations: dict[Address, BalAccountExpectation | None] = { + contract_a: ( + bal_expectation_for_contract_with_markers( + marker=marker, + staticcall_result=staticcall_result, + balance_change=tx_value, + initial_balance=initial_contract_balance, + ) + if call_value == 0 + else BalAccountExpectation( + storage_changes=[ + BalStorageSlot( + slot=0, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + BalStorageSlot( + slot=2, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + ], + storage_reads=[1], + balance_changes=contract_a_balance_changes, + ) + ), + contract_b: BalAccountExpectation.empty(), # STATICCALLed + } + + if call_value == 0: + account_expectations[precompile] = BalAccountExpectation.empty() + else: + account_expectations[precompile] = None # reverted before accessed + + bal_expectation = BlockAccessListExpectation( + account_expectations=account_expectations + ) + + state_test( + pre=pre, + tx=Transaction( + sender=alice, + to=contract_a, + gas_limit=500_000, + value=tx_value, + protected=True, + ), + expected_block_access_list=bal_expectation, + post={ + contract_a: Account( + balance=initial_contract_balance + tx_value, + storage={ + 0: marker, + 1: staticcall_result, + 2: marker, + }, + ), + contract_b: Account(balance=initial_contract_balance), + }, + ) + + +@pytest.mark.with_all_precompiles +@pytest.mark.parametrize( + "call_value", [0, 2], ids=["zero_value", "nonzero_value"] +) +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromCalledContractFiller.yml", + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromCalledContractFiller.yml", + ], +) +@pytest.mark.valid_from("Byzantium") +def test_staticcall_nested_call_to_precompile( + pre: Alloc, + state_test: StateTestFiller, + precompile: Address, + call_value: int, + fork: Fork, +) -> None: + """ + Test STATICCALL behavior with an extra call depth layer. + + Contract B (target) receives tx and CALLs contract A. + Contract A STATICCALLs contract C. + Contract C attempts to CALL the precompile. + With value = 0, this succeeds. With value > 0, this fails (static context). + """ + alice = pre.fund_eoa() + + initial_contract_balance = 1000 + marker = 0xFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEED + + # Contract C: attempts CALL to precompile with the parametrized value + contract_c = pre.deploy_contract( + code=Op.CALL(gas=100_000, address=precompile, value=call_value), + balance=initial_contract_balance, + ) + + # Contract A: STATICCALLs contract C, stores markers and result + contract_a = pre.deploy_contract( + code=( + Op.SSTORE(0, marker) + + Op.SSTORE(1, Op.STATICCALL(gas=200_000, address=contract_c)) + + Op.SSTORE(2, marker) + ), + balance=initial_contract_balance, + ) + + # Contract B (target): CALLs contract A, stores markers and result + contract_b = pre.deploy_contract( + code=( + Op.SSTORE(0, marker) + + Op.SSTORE(1, Op.CALL(gas=300_000, address=contract_a)) + + Op.SSTORE(2, marker) + ), + balance=initial_contract_balance, + ) + + tx_value = 100 + staticcall_result = 1 if call_value == 0 else 0 + + bal_expectation = None + if fork.header_bal_hash_required(): + # slot 1 read when call_value > 0 + account_expectations: dict[Address, BalAccountExpectation | None] = { + contract_a: ( + bal_expectation_for_contract_with_markers( + marker=marker, + staticcall_result=staticcall_result, + ) + if call_value == 0 + else BalAccountExpectation( + storage_changes=[ + BalStorageSlot( + slot=0, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + BalStorageSlot( + slot=2, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + ], + storage_reads=[1], + ) + ), + contract_b: bal_expectation_for_contract_with_markers( + marker=marker, + staticcall_result=1, # CALL to A always succeeds + balance_change=tx_value, + initial_balance=initial_contract_balance, + ), + contract_c: BalAccountExpectation.empty(), # STATICCALLed + } + + if call_value == 0: + account_expectations[precompile] = BalAccountExpectation.empty() + else: + account_expectations[precompile] = None # reverted before accessed + + bal_expectation = BlockAccessListExpectation( + account_expectations=account_expectations + ) + + state_test( + pre=pre, + tx=Transaction( + sender=alice, + to=contract_b, + gas_limit=500_000, + value=tx_value, + protected=True, + ), + expected_block_access_list=bal_expectation, + post={ + contract_a: Account( + balance=initial_contract_balance, + storage={ + 0: marker, + # only succeeds if call_value == 0 + 1: staticcall_result, + 2: marker, + }, + ), + contract_b: Account( + balance=initial_contract_balance + tx_value, + storage={ + 0: marker, + 1: 1, # CALL to A always succeeds + 2: marker, + }, + ), + contract_c: Account(balance=initial_contract_balance), + }, + ) + + +@pytest.mark.with_all_precompiles +@pytest.mark.parametrize( + "call_value", [0, 2], ids=["zero_value", "nonzero_value"] +) +@pytest.mark.ported_from( + [ + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromContractInitializationFiller.yml", + "https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromContractInitializationFiller.yml", + ], +) +@pytest.mark.parametrize( + "create_opcode", + [ + pytest.param(Op.CREATE, marks=pytest.mark.valid_from("Byzantium")), + pytest.param( + Op.CREATE2, marks=pytest.mark.valid_from("Constantinople") + ), + ], +) +def test_staticcall_call_to_precompile_from_contract_init( + pre: Alloc, + state_test: StateTestFiller, + precompile: Address, + call_value: int, + create_opcode: Op, + fork: Fork, +) -> None: + """ + Test STATICCALL behavior during contract initialization (CREATE). + + Contract A CREATEs a new contract whose init code STATICCALLs contract B. + Contract B attempts to CALL the precompile. + With value = 0, this succeeds. With value > 0, this fails in static + context. + """ + alice = pre.fund_eoa() + marker = 0xFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEEDFEED + + contract_initial_balance = 1000 + + # Contract B: attempts CALL to precompile with the parametrized value + contract_b = pre.deploy_contract( + code=Op.CALL(gas=100_000, address=precompile, value=call_value), + balance=contract_initial_balance, + ) + + # Init code: stores markers and STATICCALL result during initialization + # Note: storage written during init but no return means the created + # contract will have empty code. + initcode = ( + Op.SSTORE(0, marker) + + Op.SSTORE(1, Op.STATICCALL(gas=200_000, address=contract_b)) + + Op.SSTORE(2, marker) + ) + + # Contract A: CREATEs new contract using init_code from calldata + contract_a = pre.deploy_contract( + code=( + Op.SSTORE(0, marker) + + Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + + Op.SSTORE( + 1, + create_opcode(value=0, offset=0, size=Op.CALLDATASIZE), + ) + + Op.SSTORE(2, marker) + ), + balance=contract_initial_balance, + ) + created_contract = compute_create_address( + nonce=1, + address=contract_a, + opcode=create_opcode, + initcode=initcode, + ) + + tx_value = 100 + staticcall_result = 1 if call_value == 0 else 0 + + bal_expectation = None + if fork.header_bal_hash_required(): + # stores created_contract in slot 1, receives tx value + account_expectations: dict[Address, BalAccountExpectation | None] = { + contract_a: BalAccountExpectation( + storage_changes=[ + BalStorageSlot( + slot=0, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + BalStorageSlot( + slot=1, + slot_changes=[ + BalStorageChange( + block_access_index=1, + post_value=created_contract, + ) + ], + ), + BalStorageSlot( + slot=2, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + ], + balance_changes=[ + BalBalanceChange( + block_access_index=1, + post_balance=contract_initial_balance + tx_value, + ) + ], + nonce_changes=[ + BalNonceChange(block_access_index=1, post_nonce=2) + ], + ), + contract_b: BalAccountExpectation.empty(), # STATICCALLed + } + + # slot 1 read when call_value > 0 + created_nonce_changes = [ + BalNonceChange(block_access_index=1, post_nonce=1) + ] + account_expectations[created_contract] = ( + BalAccountExpectation( + storage_changes=bal_marker_storage_changes( + marker, staticcall_result + ), + nonce_changes=created_nonce_changes, + ) + if call_value == 0 + else BalAccountExpectation( + storage_changes=[ + BalStorageSlot( + slot=0, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + BalStorageSlot( + slot=2, + slot_changes=[ + BalStorageChange( + block_access_index=1, post_value=marker + ) + ], + ), + ], + storage_reads=[1], + nonce_changes=created_nonce_changes, + ) + ) + + if call_value == 0: + account_expectations[precompile] = BalAccountExpectation.empty() + else: + account_expectations[precompile] = None # reverted before accessed + + bal_expectation = BlockAccessListExpectation( + account_expectations=account_expectations + ) + + state_test( + pre=pre, + tx=Transaction( + sender=alice, + to=contract_a, + gas_limit=4_000_000, + value=tx_value, + data=bytes(initcode), + protected=True, + ), + expected_block_access_list=bal_expectation, + post={ + contract_a: Account( + balance=contract_initial_balance + tx_value, + storage={0: marker, 1: created_contract, 2: marker}, + ), + created_contract: Account( + storage={ + 0: marker, + # only succeeds if call_value == 0 + 1: staticcall_result, + 2: marker, + }, + code=b"", + ), + contract_b: Account(balance=contract_initial_balance), + }, + ) diff --git a/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromCalledContractFiller.yml b/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromCalledContractFiller.yml deleted file mode 100644 index 5ae5aabbafd..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromCalledContractFiller.yml +++ /dev/null @@ -1,197 +0,0 @@ ---- -CallWithNOTZeroValueToPrecompileFromCalledContract: - _info: - comment: | - Contract B calls contract A. - Contract A staticcalls contract CALL-00X. - Contract CALL-00X calls precompiled contracts with non zero value. - It should interrupt for each precompiled contract. - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: - - !!int 0 - - !!int 1 - - !!int 2 - - !!int 3 - - !!int 4 - - !!int 5 - - !!int 6 - - !!int 7 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - : - balance: '1000' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x02': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - : - balance: '1100' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0x01' - '0x02': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - pre: - : - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x02': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [ 0 ] (CALLDATALOAD 0) - [[ 0x01 ]] (CALL (GAS) 0 0 32 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - } - : - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x02': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) (CALLDATALOAD 0) 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Recovery of ECDSA signature - [ 0x00 ] 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c - [ 0x20 ] 28 - [ 0x40 ] 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f - [ 0x60 ] 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549 - (CALL (GAS) 1 2 0 128 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Hash function SHA256 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 2 2 0 32 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Hash function RIPEMD160 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 3 2 0 32 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Identity - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 4 2 0 32 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Modular exponentiation - [ 0x00 ] 1 - [ 0x20 ] 32 - [ 0x40 ] 32 - [ 0x60 ] 0x03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0x80 ] 0x2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0xa0 ] 0x2f00000000000000000000000000000000000000000000000000000000000000 - (CALL (GAS) 5 2 0 0xa1 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Addition on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286 - [ 0x60 ] 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4 - (CALL (GAS) 6 2 0 128 0x2000 64) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Scalar multiplication on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000003 - (CALL (GAS) 7 2 0 96 0x2000 64) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Checking a pairing equation on curve alt_bn128 - ;; proof.A + vk.A + negateG1(proof.Ap) + P2 - [ 0x0000 ] 0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59 - [ 0x0020 ] 0x3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41 - [ 0x0040 ] 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7 - [ 0x0060 ] 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678 - [ 0x0080 ] 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d - [ 0x00a0 ] 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550 - [ 0x00c0 ] 0x111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c - [ 0x00e0 ] 0x2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411 - [ 0x0100 ] 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - [ 0x0120 ] 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - [ 0x0140 ] 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - [ 0x0160 ] 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - (CALL (GAS) 8 2 0 0x0180 0x2000 32) - } - : - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - gasLimit: - - '4000000' - gasPrice: '10' - nonce: '0' - secretKey: '' - to: '' - value: - - '100' diff --git a/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromContractInitializationFiller.yml b/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromContractInitializationFiller.yml deleted file mode 100644 index 8dc042d5542..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromContractInitializationFiller.yml +++ /dev/null @@ -1,207 +0,0 @@ ---- -CallWithNOTZeroValueToPrecompileFromContractInitialization: - _info: - comment: | - Contract A creates new contract. - New contract initialization code staticcalls contract CALL-00X. - Contract CALL-00X calls precompiled contracts with non zero value. - It should interrupt for each precompiled contract. - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: - - !!int 0 - - !!int 1 - - !!int 2 - - !!int 3 - - !!int 4 - - !!int 5 - - !!int 6 - - !!int 7 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - a000000000000000000000000000000000000000: - balance: '1100' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0xf7b0c3906cdfc0b8a638d274d3bcbbd6318e9ac1' - '0x02': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - f7b0c3906cdfc0b8a638d274d3bcbbd6318e9ac1: - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x02': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - pre: - a000000000000000000000000000000000000000: - balance: '1000' - nonce: '1' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x02': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - (CALLDATACOPY 0 0 (CALLDATASIZE)) - [[ 0x01 ]] (CREATE 0 0 (CALLDATASIZE)) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - } - ca11001000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Recovery of ECDSA signature - [ 0x00 ] 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c - [ 0x20 ] 28 - [ 0x40 ] 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f - [ 0x60 ] 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549 - (CALL (GAS) 1 2 0 128 0x2000 32) - } - ca11002000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Hash function SHA256 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 2 2 0 32 0x2000 32) - } - ca11003000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Hash function RIPEMD160 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 3 2 0 32 0x2000 32) - } - ca11004000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Identity - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 4 2 0 32 0x2000 32) - } - ca11005000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Modular exponentiation - [ 0x00 ] 1 - [ 0x20 ] 32 - [ 0x40 ] 32 - [ 0x60 ] 0x03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0x80 ] 0x2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0xa0 ] 0x2f00000000000000000000000000000000000000000000000000000000000000 - (CALL (GAS) 5 2 0 0xa1 0x2000 32) - } - ca11006000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Addition on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286 - [ 0x60 ] 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4 - (CALL (GAS) 6 2 0 128 0x2000 64) - } - ca11007000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Scalar multiplication on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000003 - (CALL (GAS) 7 2 0 96 0x2000 64) - } - ca11008000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Checking a pairing equation on curve alt_bn128 - ;; proof.A + vk.A + negateG1(proof.Ap) + P2 - [ 0x0000 ] 0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59 - [ 0x0020 ] 0x3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41 - [ 0x0040 ] 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7 - [ 0x0060 ] 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678 - [ 0x0080 ] 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d - [ 0x00a0 ] 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550 - [ 0x00c0 ] 0x111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c - [ 0x00e0 ] 0x2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411 - [ 0x0100 ] 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - [ 0x0120 ] 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - [ 0x0140 ] 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - [ 0x0160 ] 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - (CALL (GAS) 8 2 0 0x0180 0x2000 32) - } - a94f5374fce5edbc8e2a8697c15331677e6ebf0b: - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11001000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11002000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11003000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11004000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11005000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11006000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11007000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - - | - { [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) 0xca11008000000000000000000000000000000000 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed } - gasLimit: - - '4000000' - gasPrice: '10' - nonce: '0' - secretKey: '0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8' - to: 'a000000000000000000000000000000000000000' - value: - - '100' diff --git a/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromTransactionFiller.yml b/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromTransactionFiller.yml deleted file mode 100644 index b9e571025f1..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/CallWithNOTZeroValueToPrecompileFromTransactionFiller.yml +++ /dev/null @@ -1,176 +0,0 @@ ---- -CallWithNOTZeroValueToPrecompileFromTransaction: - _info: - comment: | - Contract A staticcalls contract CALL-00X. - Contract CALL-00X calls precompiled contracts with non zero value. - It should interrupt for each precompiled contract. - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: - - !!int 0 - - !!int 1 - - !!int 2 - - !!int 3 - - !!int 4 - - !!int 5 - - !!int 6 - - !!int 7 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - : - balance: '1100' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x02': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - pre: - : - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x02': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - [[ 0x01 ]] (STATICCALL (GAS) (CALLDATALOAD 0) 0 0 0 0) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Recovery of ECDSA signature - [ 0x00 ] 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c - [ 0x20 ] 28 - [ 0x40 ] 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f - [ 0x60 ] 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549 - (CALL (GAS) 1 2 0 128 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Hash function SHA256 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 2 2 0 32 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Hash function RIPEMD160 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 3 2 0 32 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Identity - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - (CALL (GAS) 4 2 0 32 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Modular exponentiation - [ 0x00 ] 1 - [ 0x20 ] 32 - [ 0x40 ] 32 - [ 0x60 ] 0x03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0x80 ] 0x2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0xa0 ] 0x2f00000000000000000000000000000000000000000000000000000000000000 - (CALL (GAS) 5 2 0 0xa1 0x2000 32) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Addition on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286 - [ 0x60 ] 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4 - (CALL (GAS) 6 2 0 128 0x2000 64) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Scalar multiplication on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000003 - (CALL (GAS) 7 2 0 96 0x2000 64) - } - : - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Checking a pairing equation on curve alt_bn128 - ;; proof.A + vk.A + negateG1(proof.Ap) + P2 - [ 0x0000 ] 0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59 - [ 0x0020 ] 0x3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41 - [ 0x0040 ] 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7 - [ 0x0060 ] 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678 - [ 0x0080 ] 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d - [ 0x00a0 ] 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550 - [ 0x00c0 ] 0x111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c - [ 0x00e0 ] 0x2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411 - [ 0x0100 ] 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - [ 0x0120 ] 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - [ 0x0140 ] 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - [ 0x0160 ] 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - (CALL (GAS) 8 2 0 0x0180 0x2000 32) - } - : - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - - ':raw 0x000000000000000000000000' - gasLimit: - - '4000000' - gasPrice: '10' - nonce: '0' - secretKey: '' - to: '' - value: - - '100' diff --git a/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromCalledContractFiller.yml b/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromCalledContractFiller.yml deleted file mode 100644 index 0f85d3338fc..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromCalledContractFiller.yml +++ /dev/null @@ -1,234 +0,0 @@ ---- -CallWithZeroValueToPrecompileFromCalledContract: - _info: - comment: | - Contract C calls contract B. - Contract B staticcalls contract A. - Contract A calls precompiled contracts with 0 value. - It should execute successfully for each precompiled contract. - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: !!int -1 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - a000000000000000000000000000000000000000: - balance: '1000' - storage: {} - c000000000000000000000000000000000000000: - balance: '1100' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - b000000000000000000000000000000000000000: - balance: '1000' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - # precompiled contract #1 - Recovery of ECDSA signature - '0x0a00': '0x01' - '0x0a01': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b' - '0x0a02': '0x01' - # precompiled contract #2 - Hash function SHA256 - '0x0a03': '0x01' - '0x0a04': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - '0x0a05': '0x73f5062fb68ed2a1ec82ff8c73f9251bb9cf53a623bc93527e16bc5ae29dad74' - # precompiled contract #3 - Hash function RIPEMD160 - '0x0a06': '0x01' - '0x0a07': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - '0x0a08': '0x14ef238cfa4075e9ede92f18b1566c1dd0b99aaa' - # precompiled contract #4 - Identity - '0x0a09': '0x01' - '0x0a10': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - # precompiled contract #5 - Modular exponentiation - '0x0a11': '0x01' - '0x0a12': '0x01' - # precompiled contract #6 - Addition on elliptic curve alt_bn128 - '0x0a13': '0x01' - '0x0a14': '0x1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49' - '0x0a15': '0x018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f' - # precompiled contract #7 - Scalar multiplication on elliptic curve alt_bn128 - '0x0a16': '0x01' - '0x0a17': '0x1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49' - '0x0a18': '0x018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f' - # precompiled contract #8 - Checking a pairing equation on curve alt_bn128 - '0x0a19': '0x01' - '0x0a20': '0x01' - pre: - c000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - (CALL (GAS) 0xb000000000000000000000000000000000000000 0 0 0 0 0) - [[ 0x01 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - } - b000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - (STATICCALL (GAS) 0xa000000000000000000000000000000000000000 0 0 0x0a0000 0x012020) - [[ 0x01 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - ;; save results to store - [[ 0x0a00 ]] @0x0a0000 [[ 0x0a11 ]] @0x0a1100 - [[ 0x0a01 ]] @0x0a0100 [[ 0x0a12 ]] @0x0a1200 - [[ 0x0a02 ]] @0x0a0200 [[ 0x0a13 ]] @0x0a1300 - [[ 0x0a03 ]] @0x0a0300 [[ 0x0a14 ]] @0x0a1400 - [[ 0x0a04 ]] @0x0a0400 [[ 0x0a15 ]] @0x0a1500 - [[ 0x0a05 ]] @0x0a0500 [[ 0x0a16 ]] @0x0a1600 - [[ 0x0a06 ]] @0x0a0600 [[ 0x0a17 ]] @0x0a1700 - [[ 0x0a07 ]] @0x0a0700 [[ 0x0a18 ]] @0x0a1800 - [[ 0x0a08 ]] @0x0a0800 [[ 0x0a19 ]] @0x0a1900 - [[ 0x0a09 ]] @0x0a0900 [[ 0x0a20 ]] @0x0a2000 - [[ 0x0a10 ]] @0x0a1000 - } - a000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Recovery of ECDSA signature - [ 0x00 ] 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c - [ 0x20 ] 28 - [ 0x40 ] 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f - [ 0x60 ] 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549 - ;; zero value - [ 0x0a0000 ] (CALL (GAS) 1 0 0 128 0x2000 32) - [ 0x0a0100 ] (MOD @0x2000 (EXP 2 160)) - [ 0x0a0200 ] (EQ (ORIGIN) @0x0a0100) - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Hash function SHA256 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0300 ] (CALL (GAS) 2 0 0 32 0x2000 32) - [ 0x0a0400 ] @0 - [ 0x0a0500 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Hash function RIPEMD160 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0600 ] (CALL (GAS) 3 0 0 32 0x2000 32) - [ 0x0a0700 ] @0 - [ 0x0a0800 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Identity - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0900 ] (CALL (GAS) 4 0 0 32 0x2000 32) - [ 0x0a1000 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Modular exponentiation - [ 0x00 ] 1 - [ 0x20 ] 32 - [ 0x40 ] 32 - [ 0x60 ] 0x03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0x80 ] 0x2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0xa0 ] 0x2f00000000000000000000000000000000000000000000000000000000000000 - ;; zero value - [ 0x0a1100 ] (CALL (GAS) 5 0 0 0xa1 0x2000 32) - [ 0x0a1200 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x80 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0xa0 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Addition on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286 - [ 0x60 ] 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4 - ;; zero value - [ 0x0a1300 ] (CALL (GAS) 6 0 0 128 0x2000 64) - [ 0x0a1400 ] @0x2000 - [ 0x0a1500 ] @0x2020 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2020 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Scalar multiplication on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000003 - ;; zero value - [ 0x0a1600 ] (CALL (GAS) 7 0 0 96 0x2000 64) - [ 0x0a1700 ] @0x2000 - [ 0x0a1800 ] @0x2020 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2020 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Checking a pairing equation on curve alt_bn128 - ;; proof.A + vk.A + negateG1(proof.Ap) + P2 - [ 0x0000 ] 0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59 - [ 0x0020 ] 0x3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41 - [ 0x0040 ] 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7 - [ 0x0060 ] 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678 - [ 0x0080 ] 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d - [ 0x00a0 ] 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550 - [ 0x00c0 ] 0x111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c - [ 0x00e0 ] 0x2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411 - [ 0x0100 ] 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - [ 0x0120 ] 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - [ 0x0140 ] 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - [ 0x0160 ] 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ;; zero value - [ 0x0a1900 ] (CALL (GAS) 8 0 0 0x0180 0x2000 32) - [ 0x0a2000 ] @0x2000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; return all computed values to caller for checking - (RETURN 0x0a0000 0x012020) - } - a94f5374fce5edbc8e2a8697c15331677e6ebf0b: - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - '' - gasLimit: - - '4000000' - gasPrice: '10' - nonce: '0' - secretKey: '0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8' - to: 'c000000000000000000000000000000000000000' - value: - - '100' diff --git a/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromContractInitializationFiller.yml b/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromContractInitializationFiller.yml deleted file mode 100644 index 9417a6ed6d7..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromContractInitializationFiller.yml +++ /dev/null @@ -1,226 +0,0 @@ ---- -CallWithZeroValueToPrecompileFromContractInitialization: - _info: - comment: | - Contract B creates new contract. - New contract initialization code staticcalls contract A. - Contract A calls precompiled contracts with 0 value. - It should execute successfully for each precompiled contract. - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: - - !!int 0 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - b000000000000000000000000000000000000000: - balance: '1100' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0xfd7776b1a634b0dc19301b174ccf30d4d24070a8' - '0x02': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - fd7776b1a634b0dc19301b174ccf30d4d24070a8: - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - # precompiled contract #1 - Recovery of ECDSA signature - '0x0a00': '0x01' - '0x0a01': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b' - '0x0a02': '0x01' - # precompiled contract #2 - Hash function SHA256 - '0x0a03': '0x01' - '0x0a04': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - '0x0a05': '0x73f5062fb68ed2a1ec82ff8c73f9251bb9cf53a623bc93527e16bc5ae29dad74' - # precompiled contract #3 - Hash function RIPEMD160 - '0x0a06': '0x01' - '0x0a07': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - '0x0a08': '0x14ef238cfa4075e9ede92f18b1566c1dd0b99aaa' - # precompiled contract #4 - Identity - '0x0a09': '0x01' - '0x0a10': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - # precompiled contract #5 - Modular exponentiation - '0x0a11': '0x01' - '0x0a12': '0x01' - # precompiled contract #6 - Addition on elliptic curve alt_bn128 - '0x0a13': '0x01' - '0x0a14': '0x1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49' - '0x0a15': '0x018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f' - # precompiled contract #7 - Scalar multiplication on elliptic curve alt_bn128 - '0x0a16': '0x01' - '0x0a17': '0x1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49' - '0x0a18': '0x018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f' - # precompiled contract #8 - Checking a pairing equation on curve alt_bn128 - '0x0a19': '0x01' - '0x0a20': '0x01' - pre: - b000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - (CALLDATACOPY 0 0 (CALLDATASIZE)) - [[ 0x01 ]] (CREATE2 0 0 (CALLDATASIZE) 0x5a175a175a17) - [[ 0x02 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - } - a000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Recovery of ECDSA signature - [ 0x00 ] 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c - [ 0x20 ] 28 - [ 0x40 ] 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f - [ 0x60 ] 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549 - ;; zero value - [ 0x0a0000 ] (CALL (GAS) 1 0 0 128 0x2000 32) - [ 0x0a0100 ] (MOD @0x2000 (EXP 2 160)) - [ 0x0a0200 ] (EQ (ORIGIN) @0x0a0100) - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Hash function SHA256 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0300 ] (CALL (GAS) 2 0 0 32 0x2000 32) - [ 0x0a0400 ] @0 - [ 0x0a0500 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Hash function RIPEMD160 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0600 ] (CALL (GAS) 3 0 0 32 0x2000 32) - [ 0x0a0700 ] @0 - [ 0x0a0800 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Identity - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0900 ] (CALL (GAS) 4 0 0 32 0x2000 32) - [ 0x0a1000 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Modular exponentiation - [ 0x00 ] 1 - [ 0x20 ] 32 - [ 0x40 ] 32 - [ 0x60 ] 0x03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0x80 ] 0x2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0xa0 ] 0x2f00000000000000000000000000000000000000000000000000000000000000 - ;; zero value - [ 0x0a1100 ] (CALL (GAS) 5 0 0 0xa1 0x2000 32) - [ 0x0a1200 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x80 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0xa0 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Addition on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286 - [ 0x60 ] 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4 - ;; zero value - [ 0x0a1300 ] (CALL (GAS) 6 0 0 128 0x2000 64) - [ 0x0a1400 ] @0x2000 - [ 0x0a1500 ] @0x2020 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2020 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Scalar multiplication on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000003 - ;; zero value - [ 0x0a1600 ] (CALL (GAS) 7 0 0 96 0x2000 64) - [ 0x0a1700 ] @0x2000 - [ 0x0a1800 ] @0x2020 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2020 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Checking a pairing equation on curve alt_bn128 - ;; proof.A + vk.A + negateG1(proof.Ap) + P2 - [ 0x0000 ] 0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59 - [ 0x0020 ] 0x3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41 - [ 0x0040 ] 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7 - [ 0x0060 ] 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678 - [ 0x0080 ] 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d - [ 0x00a0 ] 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550 - [ 0x00c0 ] 0x111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c - [ 0x00e0 ] 0x2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411 - [ 0x0100 ] 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - [ 0x0120 ] 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - [ 0x0140 ] 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - [ 0x0160 ] 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ;; zero value - [ 0x0a1900 ] (CALL (GAS) 8 0 0 0x0180 0x2000 32) - [ 0x0a2000 ] @0x2000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; return all computed values to caller for checking - (RETURN 0x0a0000 0x012020) - } - a94f5374fce5edbc8e2a8697c15331677e6ebf0b: - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - (STATICCALL (GAS) 0xa000000000000000000000000000000000000000 0 0 0x0a0000 0x012020) - [[ 0x01 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - ;; save results to store - [[ 0x0a00 ]] @0x0a0000 [[ 0x0a11 ]] @0x0a1100 - [[ 0x0a01 ]] @0x0a0100 [[ 0x0a12 ]] @0x0a1200 - [[ 0x0a02 ]] @0x0a0200 [[ 0x0a13 ]] @0x0a1300 - [[ 0x0a03 ]] @0x0a0300 [[ 0x0a14 ]] @0x0a1400 - [[ 0x0a04 ]] @0x0a0400 [[ 0x0a15 ]] @0x0a1500 - [[ 0x0a05 ]] @0x0a0500 [[ 0x0a16 ]] @0x0a1600 - [[ 0x0a06 ]] @0x0a0600 [[ 0x0a17 ]] @0x0a1700 - [[ 0x0a07 ]] @0x0a0700 [[ 0x0a18 ]] @0x0a1800 - [[ 0x0a08 ]] @0x0a0800 [[ 0x0a19 ]] @0x0a1900 - [[ 0x0a09 ]] @0x0a0900 [[ 0x0a20 ]] @0x0a2000 - [[ 0x0a10 ]] @0x0a1000 - } - gasLimit: - - '4000000' - gasPrice: '10' - nonce: '0' - secretKey: '0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8' - to: 'b000000000000000000000000000000000000000' - value: - - '100' diff --git a/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromTransactionFiller.yml b/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromTransactionFiller.yml deleted file mode 100644 index c34292cd2e0..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/CallWithZeroValueToPrecompileFromTransactionFiller.yml +++ /dev/null @@ -1,216 +0,0 @@ ---- -CallWithZeroValueToPrecompileFromTransaction: - _info: - comment: | - Contract B staticcalls contract A. - Contract A calls precompiled contracts with 0 value. - It should execute successfully for each precompiled contract. - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: !!int -1 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - a000000000000000000000000000000000000000: - balance: '1000' - storage: {} - b000000000000000000000000000000000000000: - balance: '1100' - storage: - '0x00': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - '0x01': '0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed' - # precompiled contract #1 - Recovery of ECDSA signature - '0x0a00': '0x01' - '0x0a01': '0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b' - '0x0a02': '0x01' - # precompiled contract #2 - Hash function SHA256 - '0x0a03': '0x01' - '0x0a04': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - '0x0a05': '0x73f5062fb68ed2a1ec82ff8c73f9251bb9cf53a623bc93527e16bc5ae29dad74' - # precompiled contract #3 - Hash function RIPEMD160 - '0x0a06': '0x01' - '0x0a07': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - '0x0a08': '0x14ef238cfa4075e9ede92f18b1566c1dd0b99aaa' - # precompiled contract #4 - Identity - '0x0a09': '0x01' - '0x0a10': '0x0ccccccccccccccccccccccccccccccccccccccccccccccccccc000000' - # precompiled contract #5 - Modular exponentiation - '0x0a11': '0x01' - '0x0a12': '0x01' - # precompiled contract #6 - Addition on elliptic curve alt_bn128 - '0x0a13': '0x01' - '0x0a14': '0x1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49' - '0x0a15': '0x018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f' - # precompiled contract #7 - Scalar multiplication on elliptic curve alt_bn128 - '0x0a16': '0x01' - '0x0a17': '0x1f4d1d80177b1377743d1901f70d7389be7f7a35a35bfd234a8aaee615b88c49' - '0x0a18': '0x018683193ae021a2f8920fed186cde5d9b1365116865281ccf884c1f28b1df8f' - # precompiled contract #8 - Checking a pairing equation on curve alt_bn128 - '0x0a19': '0x01' - '0x0a20': '0x01' - pre: - b000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: - '0x00': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - '0x01': '0xdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeafdeadbeaf' - code: | - { - [[ 0x00 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - (STATICCALL (GAS) 0xa000000000000000000000000000000000000000 0 0 0x0a0000 0x012020) - [[ 0x01 ]] 0xfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeedfeed - ;; save results to store - [[ 0x0a00 ]] @0x0a0000 [[ 0x0a11 ]] @0x0a1100 - [[ 0x0a01 ]] @0x0a0100 [[ 0x0a12 ]] @0x0a1200 - [[ 0x0a02 ]] @0x0a0200 [[ 0x0a13 ]] @0x0a1300 - [[ 0x0a03 ]] @0x0a0300 [[ 0x0a14 ]] @0x0a1400 - [[ 0x0a04 ]] @0x0a0400 [[ 0x0a15 ]] @0x0a1500 - [[ 0x0a05 ]] @0x0a0500 [[ 0x0a16 ]] @0x0a1600 - [[ 0x0a06 ]] @0x0a0600 [[ 0x0a17 ]] @0x0a1700 - [[ 0x0a07 ]] @0x0a0700 [[ 0x0a18 ]] @0x0a1800 - [[ 0x0a08 ]] @0x0a0800 [[ 0x0a19 ]] @0x0a1900 - [[ 0x0a09 ]] @0x0a0900 [[ 0x0a20 ]] @0x0a2000 - [[ 0x0a10 ]] @0x0a1000 - } - a000000000000000000000000000000000000000: - balance: '1000' - nonce: '0' - storage: {} - code: | - { - ;; Recovery of ECDSA signature - [ 0x00 ] 0x18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c - [ 0x20 ] 28 - [ 0x40 ] 0x73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75f - [ 0x60 ] 0xeeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549 - ;; zero value - [ 0x0a0000 ] (CALL (GAS) 1 0 0 128 0x2000 32) - [ 0x0a0100 ] (MOD @0x2000 (EXP 2 160)) - [ 0x0a0200 ] (EQ (ORIGIN) @0x0a0100) - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Hash function SHA256 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0300 ] (CALL (GAS) 2 0 0 32 0x2000 32) - [ 0x0a0400 ] @0 - [ 0x0a0500 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Hash function RIPEMD160 - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0600 ] (CALL (GAS) 3 0 0 32 0x2000 32) - [ 0x0a0700 ] @0 - [ 0x0a0800 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Identity - [ 0x00 ] 0x0000000ccccccccccccccccccccccccccccccccccccccccccccccccccc000000 - ;; zero value - [ 0x0a0900 ] (CALL (GAS) 4 0 0 32 0x2000 32) - [ 0x0a1000 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Modular exponentiation - [ 0x00 ] 1 - [ 0x20 ] 32 - [ 0x40 ] 32 - [ 0x60 ] 0x03fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0x80 ] 0x2efffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc - [ 0xa0 ] 0x2f00000000000000000000000000000000000000000000000000000000000000 - ;; zero value - [ 0x0a1100 ] (CALL (GAS) 5 0 0 0xa1 0x2000 32) - [ 0x0a1200 ] @0x2000 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x80 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0xa0 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Addition on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x1de49a4b0233273bba8146af82042d004f2085ec982397db0d97da17204cc286 - [ 0x60 ] 0x0217327ffc463919bef80cc166d09c6172639d8589799928761bcd9f22c903d4 - ;; zero value - [ 0x0a1300 ] (CALL (GAS) 6 0 0 128 0x2000 64) - [ 0x0a1400 ] @0x2000 - [ 0x0a1500 ] @0x2020 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x60 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2020 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Scalar multiplication on elliptic curve alt_bn128 - [ 0x00 ] 0x0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd2 - [ 0x20 ] 0x16da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000003 - ;; zero value - [ 0x0a1600 ] (CALL (GAS) 7 0 0 96 0x2000 64) - [ 0x0a1700 ] @0x2000 - [ 0x0a1800 ] @0x2020 - [ 0x00 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x20 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x40 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - [ 0x2020 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; Checking a pairing equation on curve alt_bn128 - ;; proof.A + vk.A + negateG1(proof.Ap) + P2 - [ 0x0000 ] 0x1c76476f4def4bb94541d57ebba1193381ffa7aa76ada664dd31c16024c43f59 - [ 0x0020 ] 0x3034dd2920f673e204fee2811c678745fc819b55d3e9d294e45c9b03a76aef41 - [ 0x0040 ] 0x209dd15ebff5d46c4bd888e51a93cf99a7329636c63514396b4a452003a35bf7 - [ 0x0060 ] 0x04bf11ca01483bfa8b34b43561848d28905960114c8ac04049af4b6315a41678 - [ 0x0080 ] 0x2bb8324af6cfc93537a2ad1a445cfd0ca2a71acd7ac41fadbf933c2a51be344d - [ 0x00a0 ] 0x120a2a4cf30c1bf9845f20c6fe39e07ea2cce61f0c9bb048165fe5e4de877550 - [ 0x00c0 ] 0x111e129f1cf1097710d41c4ac70fcdfa5ba2023c6ff1cbeac322de49d1b6df7c - [ 0x00e0 ] 0x2032c61a830e3c17286de9462bf242fca2883585b93870a73853face6a6bf411 - [ 0x0100 ] 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2 - [ 0x0120 ] 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - [ 0x0140 ] 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b - [ 0x0160 ] 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ;; zero value - [ 0x0a1900 ] (CALL (GAS) 8 0 0 0x0180 0x2000 32) - [ 0x0a2000 ] @0x2000 - [ 0x2000 ] 0x0000000000000000000000000000000000000000000000000000000000000000 - - ;; return all computed values to caller for checking - (RETURN 0x0a0000 0x012020) - } - a94f5374fce5edbc8e2a8697c15331677e6ebf0b: - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - '' - gasLimit: - - '4000000' - gasPrice: '10' - nonce: '0' - secretKey: '0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8' - to: 'b000000000000000000000000000000000000000' - value: - - '100' diff --git a/tests/static/state_tests/stStaticFlagEnabled/StaticcallForPrecompilesIssue683Filler.yml b/tests/static/state_tests/stStaticFlagEnabled/StaticcallForPrecompilesIssue683Filler.yml deleted file mode 100644 index 55c1471f127..00000000000 --- a/tests/static/state_tests/stStaticFlagEnabled/StaticcallForPrecompilesIssue683Filler.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- -StaticcallForPrecompilesIssue683: - _info: - comment: | - Bytecode from issue 683, that initially show the problem with staticcall in hevm. - Issue link: https://github.com/ethereum/tests/issues/683 - Pull request link with original bytecode: https://github.com/dapphub/dapptools/pull/360 - env: - currentCoinbase: '0xcafe000000000000000000000000000000000001' - currentDifficulty: '0x20000' - currentGasLimit: '10000000' - currentNumber: '1' - currentTimestamp: '1000' - expect: - - indexes: - data: !!int -1 - gas: !!int -1 - value: !!int -1 - network: - - ">=Cancun" - result: - : - balance: '1100' - storage: - '0x00': '0x01' - pre: - : - balance: '1000' - nonce: '0' - storage: {} - code: ':raw 0x600080541515601d576001815580818283305afa15601b578081fd5b005b80818283600160025af15050' - : - balance: '1000000000000000000' - code: '' - nonce: '0' - storage: {} - transaction: - data: - - '' - gasLimit: - - '1000000' - gasPrice: '10' - nonce: '0' - secretKey: '' - to: '' - value: - - '100' From 3b9b018a95b40939dcb566a53fef7e0c2abb9194 Mon Sep 17 00:00:00 2001 From: felipe Date: Wed, 21 Jan 2026 17:09:19 -0700 Subject: [PATCH 03/21] feat(tests): turn on EIP-7934 tests with BALs (fill all for amsterdam) (#2058) --- .github/configs/feature.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/configs/feature.yaml b/.github/configs/feature.yaml index 0b07782d7b5..b9404e5d59d 100644 --- a/.github/configs/feature.yaml +++ b/.github/configs/feature.yaml @@ -23,6 +23,5 @@ benchmark_fast: bal: evm-type: develop - # TODO: Turn on block rlp limit tests after making filling them more flexible. - fill-params: --fork=Amsterdam -k "not eip7934" --fill-static-tests + fill-params: --fork=Amsterdam --fill-static-tests feature_only: true From f98d725cb3209c8446e23ae79e0c2391fdd0bdb8 Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Tue, 13 Jan 2026 16:26:43 -0500 Subject: [PATCH 04/21] feat(spec-specs): Add transfer log for all `CALL*` and `SELFDESTRUCT` --- src/ethereum/forks/amsterdam/vm/__init__.py | 41 ++++++++++++++++++- .../forks/amsterdam/vm/instructions/system.py | 8 ++++ .../forks/amsterdam/vm/interpreter.py | 9 +++- 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index 3d69fbd7061..d8e70a73b63 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -18,8 +18,9 @@ from ethereum_types.bytes import Bytes, Bytes0, Bytes32 from ethereum_types.numeric import U64, U256, Uint -from ethereum.crypto.hash import Hash32 +from ethereum.crypto.hash import Hash32, keccak256 from ethereum.exceptions import EthereumException +from ethereum.utils.byte import right_pad_zero_bytes from ..block_access_lists.rlp_types import BlockAccessList from ..blocks import Log, Receipt, Withdrawal @@ -30,6 +31,7 @@ from ..trie import Trie __all__ = ("Environment", "Evm", "Message") +MAGIC_XACTION_LOG_HASH = keccak256(b"42") @dataclass @@ -203,3 +205,40 @@ def incorporate_child_on_error(evm: Evm, child_evm: Evm) -> None: evm.gas_left += child_evm.gas_left merge_on_failure(child_evm.state_changes) + +def transfer_log( + evm: Evm, + sender: Address, + recipient: Address, + transfer_amount: U256, +) -> None: + """ + Main functional unit satisfying EIP-7708. + + Parameters + ---------- + evm : + The state of the ethereum virtual machine + sender : + The account address sending the transfer + recipient : + The address of the transfer recipient account + transfer_amount : + The amount of ETH transacted + + """ + # We need to pre-pad the Address correctly before converting, + # otherwise the function will throw because the lengths are not the same + padded_sender = right_pad_zero_bytes(sender, 12) + padded_recipient = right_pad_zero_bytes(recipient, 12) + log_entry = Log( + address=evm.message.current_target, + topics=( + MAGIC_XACTION_LOG_HASH, + Hash32(padded_sender), + Hash32(padded_recipient), + ), + data=transfer_amount.to_be_bytes(), + ) + + evm.logs = evm.logs + (log_entry,) diff --git a/src/ethereum/forks/amsterdam/vm/instructions/system.py b/src/ethereum/forks/amsterdam/vm/instructions/system.py index 72f44cdf70c..1ab20060892 100644 --- a/src/ethereum/forks/amsterdam/vm/instructions/system.py +++ b/src/ethereum/forks/amsterdam/vm/instructions/system.py @@ -46,6 +46,7 @@ Message, incorporate_child_on_error, incorporate_child_on_success, + transfer_log, ) from ..exceptions import OutOfGasError, Revert, WriteInStaticContext from ..gas import ( @@ -464,6 +465,8 @@ def call(evm: Evm) -> None: ) charge_gas(evm, message_call_gas.cost + extend_memory.cost) + if evm.message.is_static and value != U256(0): + raise WriteInStaticContext evm.memory += b"\x00" * extend_memory.expand_by sender_balance = get_account(state, evm.message.current_target).balance if sender_balance < value: @@ -487,6 +490,7 @@ def call(evm: Evm) -> None: code, is_delegated, ) + transfer_log(evm, evm.message.current_target, to, value) # PROGRAM COUNTER evm.pc += Uint(1) @@ -680,6 +684,10 @@ def selfdestruct(evm: Evm) -> None: beneficiary_new_balance, ) + transfer_log( + evm, evm.message.current_target, beneficiary, originator_balance + ) + # register account for deletion only if it was created # in the same transaction if originator in state.created_accounts: diff --git a/src/ethereum/forks/amsterdam/vm/interpreter.py b/src/ethereum/forks/amsterdam/vm/interpreter.py index d73ba88a726..ee4639edadc 100644 --- a/src/ethereum/forks/amsterdam/vm/interpreter.py +++ b/src/ethereum/forks/amsterdam/vm/interpreter.py @@ -59,7 +59,7 @@ from ..vm.eoa_delegation import get_delegated_code_address, set_delegation from ..vm.gas import GAS_CODE_DEPOSIT, charge_gas from ..vm.precompiled_contracts.mapping import PRE_COMPILED_CONTRACTS -from . import Evm +from . import Evm, transfer_log from .exceptions import ( AddressCollision, ExceptionalHalt, @@ -361,6 +361,13 @@ def execute_code(message: Message, state_changes: StateChanges) -> Evm: accessed_storage_keys=message.accessed_storage_keys, state_changes=state_changes, ) + transfer_log( + evm, + message.current_target, + message.caller, + message.value, + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: if message.disable_precompiles: From e82e5a3e9f4495c8eac825f869b8d903f09c6cd2 Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Thu, 15 Jan 2026 21:25:49 -0500 Subject: [PATCH 05/21] fix(spec-specs): correct CR issues, fix formatting --- src/ethereum/forks/amsterdam/vm/__init__.py | 28 +++++++++++-------- .../forks/amsterdam/vm/instructions/system.py | 9 +++--- .../forks/amsterdam/vm/interpreter.py | 4 +-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index d8e70a73b63..e56e98426d6 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -20,7 +20,7 @@ from ethereum.crypto.hash import Hash32, keccak256 from ethereum.exceptions import EthereumException -from ethereum.utils.byte import right_pad_zero_bytes +from ethereum.utils.byte import left_pad_zero_bytes from ..block_access_lists.rlp_types import BlockAccessList from ..blocks import Log, Receipt, Withdrawal @@ -31,7 +31,10 @@ from ..trie import Trie __all__ = ("Environment", "Evm", "Message") -MAGIC_XACTION_LOG_HASH = keccak256(b"42") +TRANSFER_TOPIC = keccak256(b"Transfer(address, address, uint256)") +SYSTEM_ADDRESS = Address( + bytes.fromhex("fffffffffffffffffffffffffffffffffffffffe") +) @dataclass @@ -206,14 +209,14 @@ def incorporate_child_on_error(evm: Evm, child_evm: Evm) -> None: merge_on_failure(child_evm.state_changes) -def transfer_log( +def emit_transfer_log( evm: Evm, sender: Address, recipient: Address, transfer_amount: U256, ) -> None: """ - Main functional unit satisfying EIP-7708. + Emit a LOG3 for all ETH transfers satisfying EIP-7708. Parameters ---------- @@ -221,24 +224,25 @@ def transfer_log( The state of the ethereum virtual machine sender : The account address sending the transfer - recipient : + recipient :ce to finalize sco The address of the transfer recipient account transfer_amount : The amount of ETH transacted """ - # We need to pre-pad the Address correctly before converting, - # otherwise the function will throw because the lengths are not the same - padded_sender = right_pad_zero_bytes(sender, 12) - padded_recipient = right_pad_zero_bytes(recipient, 12) + if transfer_amount == 0: + return + + padded_sender = left_pad_zero_bytes(sender, 32) + padded_recipient = left_pad_zero_bytes(recipient, 32) log_entry = Log( - address=evm.message.current_target, + address=SYSTEM_ADDRESS, topics=( - MAGIC_XACTION_LOG_HASH, + TRANSFER_TOPIC, Hash32(padded_sender), Hash32(padded_recipient), ), - data=transfer_amount.to_be_bytes(), + data=transfer_amount.to_be_bytes32(), ) evm.logs = evm.logs + (log_entry,) diff --git a/src/ethereum/forks/amsterdam/vm/instructions/system.py b/src/ethereum/forks/amsterdam/vm/instructions/system.py index 1ab20060892..554ae3c1da1 100644 --- a/src/ethereum/forks/amsterdam/vm/instructions/system.py +++ b/src/ethereum/forks/amsterdam/vm/instructions/system.py @@ -44,9 +44,9 @@ from .. import ( Evm, Message, + emit_transfer_log, incorporate_child_on_error, incorporate_child_on_success, - transfer_log, ) from ..exceptions import OutOfGasError, Revert, WriteInStaticContext from ..gas import ( @@ -464,10 +464,11 @@ def call(evm: Evm) -> None: extra_gas, ) charge_gas(evm, message_call_gas.cost + extend_memory.cost) - if evm.message.is_static and value != U256(0): raise WriteInStaticContext evm.memory += b"\x00" * extend_memory.expand_by + + # OPERATION sender_balance = get_account(state, evm.message.current_target).balance if sender_balance < value: push(evm.stack, U256(0)) @@ -490,7 +491,7 @@ def call(evm: Evm) -> None: code, is_delegated, ) - transfer_log(evm, evm.message.current_target, to, value) + emit_transfer_log(evm, evm.message.current_target, to, value) # PROGRAM COUNTER evm.pc += Uint(1) @@ -684,7 +685,7 @@ def selfdestruct(evm: Evm) -> None: beneficiary_new_balance, ) - transfer_log( + emit_transfer_log( evm, evm.message.current_target, beneficiary, originator_balance ) diff --git a/src/ethereum/forks/amsterdam/vm/interpreter.py b/src/ethereum/forks/amsterdam/vm/interpreter.py index ee4639edadc..25e378c5852 100644 --- a/src/ethereum/forks/amsterdam/vm/interpreter.py +++ b/src/ethereum/forks/amsterdam/vm/interpreter.py @@ -59,7 +59,7 @@ from ..vm.eoa_delegation import get_delegated_code_address, set_delegation from ..vm.gas import GAS_CODE_DEPOSIT, charge_gas from ..vm.precompiled_contracts.mapping import PRE_COMPILED_CONTRACTS -from . import Evm, transfer_log +from . import Evm, emit_transfer_log from .exceptions import ( AddressCollision, ExceptionalHalt, @@ -361,7 +361,7 @@ def execute_code(message: Message, state_changes: StateChanges) -> Evm: accessed_storage_keys=message.accessed_storage_keys, state_changes=state_changes, ) - transfer_log( + emit_transfer_log( evm, message.current_target, message.caller, From cd2c01419b3c7de64367b328ddc9ed204d229c86 Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Tue, 20 Jan 2026 16:18:54 -0500 Subject: [PATCH 06/21] fix(spec-specs): inline `execute_code()` to `process_message()` --- src/ethereum/forks/amsterdam/vm/__init__.py | 1 + .../forks/amsterdam/vm/instructions/system.py | 1 - .../forks/amsterdam/vm/interpreter.py | 125 +++++++----------- 3 files changed, 51 insertions(+), 76 deletions(-) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index e56e98426d6..020475bd76e 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -209,6 +209,7 @@ def incorporate_child_on_error(evm: Evm, child_evm: Evm) -> None: merge_on_failure(child_evm.state_changes) + def emit_transfer_log( evm: Evm, sender: Address, diff --git a/src/ethereum/forks/amsterdam/vm/instructions/system.py b/src/ethereum/forks/amsterdam/vm/instructions/system.py index 554ae3c1da1..d20217e9839 100644 --- a/src/ethereum/forks/amsterdam/vm/instructions/system.py +++ b/src/ethereum/forks/amsterdam/vm/instructions/system.py @@ -491,7 +491,6 @@ def call(evm: Evm) -> None: code, is_delegated, ) - emit_transfer_log(evm, evm.message.current_target, to, value) # PROGRAM COUNTER evm.pc += Uint(1) diff --git a/src/ethereum/forks/amsterdam/vm/interpreter.py b/src/ethereum/forks/amsterdam/vm/interpreter.py index 25e378c5852..7fa999a2b24 100644 --- a/src/ethereum/forks/amsterdam/vm/interpreter.py +++ b/src/ethereum/forks/amsterdam/vm/interpreter.py @@ -45,7 +45,6 @@ set_code, ) from ..state_tracker import ( - StateChanges, capture_pre_balance, capture_pre_code, merge_on_failure, @@ -266,10 +265,32 @@ def process_message(message: Message) -> Evm: """ state = message.block_env.state - transient_storage = message.tx_env.transient_storage if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") + transient_storage = message.tx_env.transient_storage + code = message.code + valid_jump_destinations = get_valid_jump_destinations(code) + evm = Evm( + pc=Uint(0), + stack=[], + memory=bytearray(), + code=code, + gas_left=message.gas, + valid_jump_destinations=valid_jump_destinations, + logs=(), + refund_counter=0, + running=True, + message=message, + output=b"", + accounts_to_delete=set(), + return_data=b"", + error=None, + accessed_addresses=message.accessed_addresses, + accessed_storage_keys=message.accessed_storage_keys, + state_changes=message.state_changes, + ) + # take snapshot of state before processing the message begin_transaction(state, transient_storage) @@ -293,6 +314,9 @@ def process_message(message: Message) -> Evm: move_ether( state, message.caller, message.current_target, message.value ) + emit_transfer_log( + evm, message.caller, message.current_target, message.value + ) sender_new_balance = get_account(state, message.caller).balance recipient_new_balance = get_account( @@ -310,84 +334,26 @@ def process_message(message: Message) -> Evm: U256(recipient_new_balance), ) - evm = execute_code(message, message.state_changes) - if evm.error: - rollback_transaction(state, transient_storage) - if not message.is_create: - merge_on_failure(evm.state_changes) - else: - commit_transaction(state, transient_storage) - if not message.is_create: - merge_on_success(evm.state_changes) - return evm - - -def execute_code(message: Message, state_changes: StateChanges) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - state_changes : - The state changes frame to use for tracking. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ - code = message.code - valid_jump_destinations = get_valid_jump_destinations(code) - - evm = Evm( - pc=Uint(0), - stack=[], - memory=bytearray(), - code=code, - gas_left=message.gas, - valid_jump_destinations=valid_jump_destinations, - logs=(), - refund_counter=0, - running=True, - message=message, - output=b"", - accounts_to_delete=set(), - return_data=b"", - error=None, - accessed_addresses=message.accessed_addresses, - accessed_storage_keys=message.accessed_storage_keys, - state_changes=state_changes, - ) - emit_transfer_log( - evm, - message.current_target, - message.caller, - message.value, - ) - + # Execute message code and handle errors try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: - if message.disable_precompiles: - return evm - evm_trace(evm, PrecompileStart(evm.message.code_address)) - PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) - evm_trace(evm, PrecompileEnd()) - return evm + if not message.disable_precompiles: + evm_trace(evm, PrecompileStart(evm.message.code_address)) + PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) + evm_trace(evm, PrecompileEnd()) - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -397,4 +363,13 @@ def execute_code(message: Message, state_changes: StateChanges) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + rollback_transaction(state, transient_storage) + if not message.is_create: + merge_on_failure(evm.state_changes) + else: + commit_transaction(state, transient_storage) + if not message.is_create: + merge_on_success(evm.state_changes) return evm From 749581369d8998f1ab6beece8d3c85fd8e410a6d Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Wed, 21 Jan 2026 00:06:45 -0500 Subject: [PATCH 07/21] chore(spec-specs): backport changes --- .../forks/amsterdam/vm/interpreter.py | 1 - .../forks/arrow_glacier/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/berlin/vm/interpreter.py | 75 +++++++---------- .../forks/byzantium/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/cancun/vm/interpreter.py | 71 ++++++---------- .../forks/constantinople/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/dao_fork/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/frontier/vm/interpreter.py | 75 +++++++---------- .../forks/gray_glacier/vm/interpreter.py | 77 +++++++---------- .../forks/homestead/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/istanbul/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/london/vm/interpreter.py | 74 +++++++---------- .../forks/muir_glacier/vm/interpreter.py | 75 +++++++---------- src/ethereum/forks/paris/vm/interpreter.py | 71 ++++++---------- src/ethereum/forks/prague/vm/interpreter.py | 83 +++++++------------ src/ethereum/forks/shanghai/vm/interpreter.py | 71 ++++++---------- .../forks/spurious_dragon/vm/interpreter.py | 75 +++++++---------- .../forks/tangerine_whistle/vm/interpreter.py | 75 +++++++---------- 18 files changed, 475 insertions(+), 798 deletions(-) diff --git a/src/ethereum/forks/amsterdam/vm/interpreter.py b/src/ethereum/forks/amsterdam/vm/interpreter.py index 7fa999a2b24..7615e734cc6 100644 --- a/src/ethereum/forks/amsterdam/vm/interpreter.py +++ b/src/ethereum/forks/amsterdam/vm/interpreter.py @@ -341,7 +341,6 @@ def process_message(message: Message) -> Evm: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - else: while evm.running and evm.pc < ulen(evm.code): try: diff --git a/src/ethereum/forks/arrow_glacier/vm/interpreter.py b/src/ethereum/forks/arrow_glacier/vm/interpreter.py index afc8e64275f..267ff72cbb7 100644 --- a/src/ethereum/forks/arrow_glacier/vm/interpreter.py +++ b/src/ethereum/forks/arrow_glacier/vm/interpreter.py @@ -225,44 +225,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -282,24 +246,34 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -309,4 +283,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/berlin/vm/interpreter.py b/src/ethereum/forks/berlin/vm/interpreter.py index 0d3c2028e03..9f57d428f67 100644 --- a/src/ethereum/forks/berlin/vm/interpreter.py +++ b/src/ethereum/forks/berlin/vm/interpreter.py @@ -221,44 +221,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -278,24 +242,34 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -305,4 +279,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/byzantium/vm/interpreter.py b/src/ethereum/forks/byzantium/vm/interpreter.py index 1594c36f154..7237f30fe46 100644 --- a/src/ethereum/forks/byzantium/vm/interpreter.py +++ b/src/ethereum/forks/byzantium/vm/interpreter.py @@ -214,44 +214,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -269,24 +233,34 @@ def execute_code(message: Message) -> Evm: return_data=b"", error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -296,4 +270,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/cancun/vm/interpreter.py b/src/ethereum/forks/cancun/vm/interpreter.py index bf8319544a1..2399a49da94 100644 --- a/src/ethereum/forks/cancun/vm/interpreter.py +++ b/src/ethereum/forks/cancun/vm/interpreter.py @@ -218,42 +218,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state, transient_storage) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state, transient_storage) - else: - commit_transaction(state, transient_storage) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -272,24 +238,32 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state, transient_storage) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -299,4 +273,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state, transient_storage) + else: + commit_transaction(state, transient_storage) return evm diff --git a/src/ethereum/forks/constantinople/vm/interpreter.py b/src/ethereum/forks/constantinople/vm/interpreter.py index 201446f30bd..e0e028871d7 100644 --- a/src/ethereum/forks/constantinople/vm/interpreter.py +++ b/src/ethereum/forks/constantinople/vm/interpreter.py @@ -215,44 +215,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -270,24 +234,34 @@ def execute_code(message: Message) -> Evm: return_data=b"", error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -297,4 +271,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/dao_fork/vm/interpreter.py b/src/ethereum/forks/dao_fork/vm/interpreter.py index 19a7bd57826..b7e1ac6c915 100644 --- a/src/ethereum/forks/dao_fork/vm/interpreter.py +++ b/src/ethereum/forks/dao_fork/vm/interpreter.py @@ -195,44 +195,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -248,27 +212,44 @@ def execute_code(message: Message) -> Evm: accounts_to_delete=set(), error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) evm.gas_left = Uint(0) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/frontier/vm/interpreter.py b/src/ethereum/forks/frontier/vm/interpreter.py index 5a3c923f3ce..335634b006a 100644 --- a/src/ethereum/forks/frontier/vm/interpreter.py +++ b/src/ethereum/forks/frontier/vm/interpreter.py @@ -193,44 +193,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -246,27 +210,44 @@ def execute_code(message: Message) -> Evm: accounts_to_delete=set(), error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) evm.gas_left = Uint(0) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/gray_glacier/vm/interpreter.py b/src/ethereum/forks/gray_glacier/vm/interpreter.py index 851d1826fa5..afe469ce3f9 100644 --- a/src/ethereum/forks/gray_glacier/vm/interpreter.py +++ b/src/ethereum/forks/gray_glacier/vm/interpreter.py @@ -217,7 +217,7 @@ def process_message(message: Message) -> Evm: Returns ------- - evm: :py:class:`~ethereum.forks.gray_glacier.vm.Evm` + evm: :py:class:`~ethereum.forks.arrow_glacier.vm.Evm` Items containing execution specific objects """ @@ -225,44 +225,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -282,24 +246,34 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -309,4 +283,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/homestead/vm/interpreter.py b/src/ethereum/forks/homestead/vm/interpreter.py index bf064d39b07..ffdcd0f8faf 100644 --- a/src/ethereum/forks/homestead/vm/interpreter.py +++ b/src/ethereum/forks/homestead/vm/interpreter.py @@ -195,44 +195,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -248,27 +212,44 @@ def execute_code(message: Message) -> Evm: accounts_to_delete=set(), error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) evm.gas_left = Uint(0) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/istanbul/vm/interpreter.py b/src/ethereum/forks/istanbul/vm/interpreter.py index ae8ca5a3141..bec60e1da1c 100644 --- a/src/ethereum/forks/istanbul/vm/interpreter.py +++ b/src/ethereum/forks/istanbul/vm/interpreter.py @@ -221,44 +221,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -276,24 +240,34 @@ def execute_code(message: Message) -> Evm: return_data=b"", error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -303,4 +277,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/london/vm/interpreter.py b/src/ethereum/forks/london/vm/interpreter.py index fec0f45d22b..ee3e616e3f1 100644 --- a/src/ethereum/forks/london/vm/interpreter.py +++ b/src/ethereum/forks/london/vm/interpreter.py @@ -225,44 +225,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -282,24 +246,35 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -309,4 +284,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/muir_glacier/vm/interpreter.py b/src/ethereum/forks/muir_glacier/vm/interpreter.py index a5841b998d6..67b6e7ca64a 100644 --- a/src/ethereum/forks/muir_glacier/vm/interpreter.py +++ b/src/ethereum/forks/muir_glacier/vm/interpreter.py @@ -221,44 +221,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -276,24 +240,34 @@ def execute_code(message: Message) -> Evm: return_data=b"", error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -303,4 +277,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/paris/vm/interpreter.py b/src/ethereum/forks/paris/vm/interpreter.py index 957323dedae..2837130881b 100644 --- a/src/ethereum/forks/paris/vm/interpreter.py +++ b/src/ethereum/forks/paris/vm/interpreter.py @@ -214,42 +214,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -268,24 +234,32 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -295,4 +269,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/prague/vm/interpreter.py b/src/ethereum/forks/prague/vm/interpreter.py index 17349b4e3b3..6e8bd16a466 100644 --- a/src/ethereum/forks/prague/vm/interpreter.py +++ b/src/ethereum/forks/prague/vm/interpreter.py @@ -234,46 +234,12 @@ def process_message(message: Message) -> Evm: """ state = message.block_env.state - transient_storage = message.tx_env.transient_storage if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state, transient_storage) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state, transient_storage) - else: - commit_transaction(state, transient_storage) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ + transient_storage = message.tx_env.transient_storage code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -292,26 +258,34 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state, transient_storage) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + + # Execute message code and handle errors try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: - if message.disable_precompiles: - return evm - evm_trace(evm, PrecompileStart(evm.message.code_address)) - PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) - evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + if not message.disable_precompiles: + evm_trace(evm, PrecompileStart(evm.message.code_address)) + PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) + evm_trace(evm, PrecompileEnd()) + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -321,4 +295,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state, transient_storage) + else: + commit_transaction(state, transient_storage) return evm diff --git a/src/ethereum/forks/shanghai/vm/interpreter.py b/src/ethereum/forks/shanghai/vm/interpreter.py index 1b28c7e3e21..383f0d04cbf 100644 --- a/src/ethereum/forks/shanghai/vm/interpreter.py +++ b/src/ethereum/forks/shanghai/vm/interpreter.py @@ -215,42 +215,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -269,24 +235,32 @@ def execute_code(message: Message) -> Evm: accessed_addresses=message.accessed_addresses, accessed_storage_keys=message.accessed_storage_keys, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) @@ -296,4 +270,11 @@ def execute_code(message: Message) -> Evm: except Revert as error: evm_trace(evm, OpException(error)) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/spurious_dragon/vm/interpreter.py b/src/ethereum/forks/spurious_dragon/vm/interpreter.py index a25a86fc21d..5313e297892 100644 --- a/src/ethereum/forks/spurious_dragon/vm/interpreter.py +++ b/src/ethereum/forks/spurious_dragon/vm/interpreter.py @@ -212,44 +212,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -266,27 +230,44 @@ def execute_code(message: Message) -> Evm: touched_accounts=set(), error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) evm.gas_left = Uint(0) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm diff --git a/src/ethereum/forks/tangerine_whistle/vm/interpreter.py b/src/ethereum/forks/tangerine_whistle/vm/interpreter.py index f17d93d2b20..5ae6e9fe4b8 100644 --- a/src/ethereum/forks/tangerine_whistle/vm/interpreter.py +++ b/src/ethereum/forks/tangerine_whistle/vm/interpreter.py @@ -195,44 +195,8 @@ def process_message(message: Message) -> Evm: if message.depth > STACK_DEPTH_LIMIT: raise StackDepthLimitError("Stack depth limit reached") - # take snapshot of state before processing the message - begin_transaction(state) - - touch_account(state, message.current_target) - - if message.should_transfer_value and message.value != 0: - move_ether( - state, message.caller, message.current_target, message.value - ) - - evm = execute_code(message) - if evm.error: - # revert state to the last saved checkpoint - # since the message call resulted in an error - rollback_transaction(state) - else: - commit_transaction(state) - return evm - - -def execute_code(message: Message) -> Evm: - """ - Executes bytecode present in the `message`. - - Parameters - ---------- - message : - Transaction specific items. - - Returns - ------- - evm: `ethereum.vm.EVM` - Items containing execution specific objects - - """ code = message.code valid_jump_destinations = get_valid_jump_destinations(code) - evm = Evm( pc=Uint(0), stack=[], @@ -248,27 +212,44 @@ def execute_code(message: Message) -> Evm: accounts_to_delete=set(), error=None, ) + + # take snapshot of state before processing the message + begin_transaction(state) + + touch_account(state, message.current_target) + + if message.should_transfer_value and message.value != 0: + move_ether( + state, message.caller, message.current_target, message.value + ) + try: if evm.message.code_address in PRE_COMPILED_CONTRACTS: evm_trace(evm, PrecompileStart(evm.message.code_address)) PRE_COMPILED_CONTRACTS[evm.message.code_address](evm) evm_trace(evm, PrecompileEnd()) - return evm - - while evm.running and evm.pc < ulen(evm.code): - try: - op = Ops(evm.code[evm.pc]) - except ValueError as e: - raise InvalidOpcode(evm.code[evm.pc]) from e + else: + while evm.running and evm.pc < ulen(evm.code): + try: + op = Ops(evm.code[evm.pc]) + except ValueError as e: + raise InvalidOpcode(evm.code[evm.pc]) from e - evm_trace(evm, OpStart(op)) - op_implementation[op](evm) - evm_trace(evm, OpEnd()) + evm_trace(evm, OpStart(op)) + op_implementation[op](evm) + evm_trace(evm, OpEnd()) - evm_trace(evm, EvmStop(Ops.STOP)) + evm_trace(evm, EvmStop(Ops.STOP)) except ExceptionalHalt as error: evm_trace(evm, OpException(error)) evm.gas_left = Uint(0) evm.error = error + + if evm.error: + # revert state to the last saved checkpoint + # since the message call resulted in an error + rollback_transaction(state) + else: + commit_transaction(state) return evm From 9316b953f21718a5b5a8c1656401bd6b7d16b832 Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Wed, 21 Jan 2026 12:16:21 -0500 Subject: [PATCH 08/21] fix(spec-specs): trim out whitespace in topic hash to match tests --- src/ethereum/forks/amsterdam/vm/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index 020475bd76e..7f7f699112e 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -31,7 +31,7 @@ from ..trie import Trie __all__ = ("Environment", "Evm", "Message") -TRANSFER_TOPIC = keccak256(b"Transfer(address, address, uint256)") +TRANSFER_TOPIC = keccak256(b"Transfer(address,address,uint256)") SYSTEM_ADDRESS = Address( bytes.fromhex("fffffffffffffffffffffffffffffffffffffffe") ) From 474371c7152509717d84bcf9db9b15feb25f43c1 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Fri, 16 Jan 2026 11:33:31 +0000 Subject: [PATCH 09/21] feat(test-specs): add transaction log verification support --- .../testing/src/execution_testing/__init__.py | 2 + .../client_clis/clis/execution_specs.py | 1 + .../exceptions/exceptions/transaction.py | 2 + .../src/execution_testing/specs/helpers.py | 77 ++++++++- .../specs/tests/test_expect.py | 148 ++++++++++++++++++ .../execution_testing/test_types/__init__.py | 3 +- .../test_types/receipt_types.py | 18 +-- .../evm_tools/t8n/t8n_types.py | 11 ++ 8 files changed, 251 insertions(+), 11 deletions(-) diff --git a/packages/testing/src/execution_testing/__init__.py b/packages/testing/src/execution_testing/__init__.py index b10c00e3535..700c7767772 100644 --- a/packages/testing/src/execution_testing/__init__.py +++ b/packages/testing/src/execution_testing/__init__.py @@ -71,6 +71,7 @@ TestParameterGroup, TestPhaseManager, Transaction, + TransactionLog, TransactionReceipt, TransactionType, Withdrawal, @@ -185,6 +186,7 @@ "TestPrivateKey2", "Transaction", "TransactionException", + "TransactionLog", "TransactionReceipt", "TransactionTest", "TransactionTestFiller", diff --git a/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py b/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py index 7f181df9cc6..4066124e4ec 100644 --- a/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py +++ b/packages/testing/src/execution_testing/client_clis/clis/execution_specs.py @@ -227,6 +227,7 @@ class ExecutionSpecsExceptionMapper(ExceptionMapper): BlockException.SYSTEM_CONTRACT_EMPTY: "System contract address", BlockException.SYSTEM_CONTRACT_CALL_FAILED: "call failed:", BlockException.INVALID_DEPOSIT_EVENT_LAYOUT: "deposit", + TransactionException.LOG_MISMATCH: "LogMismatchError", } mapping_regex: ClassVar[Dict[ExceptionBase, str]] = { # Temporary solution for issue #1981. diff --git a/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py b/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py index 64d79e0478c..a2b90227fda 100644 --- a/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py +++ b/packages/testing/src/execution_testing/exceptions/exceptions/transaction.py @@ -194,3 +194,5 @@ class TransactionException(ExceptionBase): """ TYPE_4_TX_PRE_FORK = auto() """Transaction type 4 included before activation fork.""" + LOG_MISMATCH = auto() + """Transaction receipt logs do not match expected logs.""" diff --git a/packages/testing/src/execution_testing/specs/helpers.py b/packages/testing/src/execution_testing/specs/helpers.py index 42f1a911a57..1f99274f75e 100644 --- a/packages/testing/src/execution_testing/specs/helpers.py +++ b/packages/testing/src/execution_testing/specs/helpers.py @@ -12,7 +12,11 @@ TransactionException, UndefinedException, ) -from execution_testing.test_types import Transaction, TransactionReceipt +from execution_testing.test_types import ( + Transaction, + TransactionLog, + TransactionReceipt, +) class ExecutionContext(StrEnum): @@ -131,6 +135,29 @@ def __init__( super().__init__(message) +class LogMismatchError(Exception): + """ + Exception used when an actual log field differs from the expected one. + """ + + def __init__( + self, + index: int, + log_index: int, + field_name: str, + expected_value: Any, + actual_value: Any, + ): + """Initialize the exception.""" + message = ( + f"\nLogMismatch (pos={index}, log={log_index}):" + f"\n What: {field_name} mismatch!" + f"\n Want: {expected_value}" + f"\n Got: {actual_value}" + ) + super().__init__(message) + + @dataclass class ExceptionInfo: """Info to print transaction exception error messages.""" @@ -236,6 +263,39 @@ def __init__( ) +def verify_log( + tx_index: int, + log_index: int, + expected: TransactionLog, + actual: TransactionLog, +) -> None: + """Verify a single log matches expected values (only specified fields).""" + if expected.address is not None and expected.address != actual.address: + raise LogMismatchError( + index=tx_index, + log_index=log_index, + field_name="address", + expected_value=expected.address, + actual_value=actual.address, + ) + if expected.topics is not None and expected.topics != actual.topics: + raise LogMismatchError( + index=tx_index, + log_index=log_index, + field_name="topics", + expected_value=expected.topics, + actual_value=actual.topics, + ) + if expected.data is not None and expected.data != actual.data: + raise LogMismatchError( + index=tx_index, + log_index=log_index, + field_name="data", + expected_value=expected.data, + actual_value=actual.data, + ) + + def verify_transaction_receipt( transaction_index: int, expected_receipt: TransactionReceipt | None, @@ -261,6 +321,21 @@ def verify_transaction_receipt( expected_value=expected_receipt.gas_used, actual_value=actual_receipt.gas_used, ) + if expected_receipt.logs is not None and actual_receipt.logs is not None: + actual_logs = actual_receipt.logs + expected_logs = expected_receipt.logs + if len(expected_logs) != len(actual_logs): + raise LogMismatchError( + index=transaction_index, + log_index=0, + field_name="log_count", + expected_value=len(expected_logs), + actual_value=len(actual_logs), + ) + for log_idx, (expected, actual) in enumerate( + zip(expected_logs, actual_logs) + ): + verify_log(transaction_index, log_idx, expected, actual) # TODO: Add more fields as needed diff --git a/packages/testing/src/execution_testing/specs/tests/test_expect.py b/packages/testing/src/execution_testing/specs/tests/test_expect.py index 5cb5d0519c2..f0d309e850e 100644 --- a/packages/testing/src/execution_testing/specs/tests/test_expect.py +++ b/packages/testing/src/execution_testing/specs/tests/test_expect.py @@ -7,6 +7,8 @@ from execution_testing.base_types import ( Account, Address, + Bytes, + Hash, Storage, TestAddress, TestPrivateKey, @@ -24,15 +26,18 @@ Alloc, Environment, Transaction, + TransactionLog, TransactionReceipt, ) from ..blockchain import BlockchainEngineFixture, BlockchainTest from ..helpers import ( ExecutionExceptionMismatchError, + LogMismatchError, TransactionReceiptMismatchError, UnexpectedExecutionFailError, UnexpectedExecutionSuccessError, + verify_log, ) from ..state import StateTest @@ -543,3 +548,146 @@ def test_block_intermediate_state( post=block_3.expected_post_state, blocks=[block_1, block_2, block_3], ).generate(t8n=default_t8n, fixture_format=fixture_format) + + +# Log verification tests +@pytest.mark.parametrize( + "expected_log,actual_log,should_raise", + [ + pytest.param( + TransactionLog( + address=Address(0x100), + topics=[Hash(b"\x01" * 32)], + data=Bytes(b"\x02" * 32), + ), + TransactionLog( + address=Address(0x100), + topics=[Hash(b"\x01" * 32)], + data=Bytes(b"\x02" * 32), + ), + False, + id="matching_logs", + ), + pytest.param( + TransactionLog( + address=Address(0x100), + ), + TransactionLog( + address=Address(0x200), + topics=[Hash(b"\x01" * 32)], + data=Bytes(b"\x02" * 32), + ), + True, + id="address_mismatch", + ), + pytest.param( + TransactionLog( + topics=[Hash(b"\x01" * 32)], + ), + TransactionLog( + address=Address(0x100), + topics=[Hash(b"\x02" * 32)], + data=Bytes(b"\x02" * 32), + ), + True, + id="topics_mismatch", + ), + pytest.param( + TransactionLog( + data=Bytes(b"\x01" * 32), + ), + TransactionLog( + address=Address(0x100), + topics=[Hash(b"\x01" * 32)], + data=Bytes(b"\x02" * 32), + ), + True, + id="data_mismatch", + ), + pytest.param( + TransactionLog( + address=None, + topics=None, + data=None, + ), + TransactionLog( + address=Address(0x100), + topics=[Hash(b"\x01" * 32)], + data=Bytes(b"\x02" * 32), + ), + False, + id="no_fields_specified", + ), + ], +) +def test_verify_log( + expected_log: TransactionLog, + actual_log: TransactionLog, + should_raise: bool, +) -> None: + """Test verify_log function for log field mismatches.""" + if should_raise: + with pytest.raises(LogMismatchError): + verify_log(0, 0, expected_log, actual_log) + else: + verify_log(0, 0, expected_log, actual_log) + + +# Log mismatch integration tests using Amsterdam fork (EIP-7708) +@pytest.mark.parametrize( + "mismatch_type", + [ + pytest.param("address", id="log_address_mismatch"), + pytest.param("topics", id="log_topics_mismatch"), + pytest.param("data", id="log_data_mismatch"), + ], +) +def test_log_mismatch_during_generation( + default_t8n: TransitionTool, + mismatch_type: str, +) -> None: + """Test that log mismatches raise LogMismatchError during test generation.""" + from execution_testing.forks import Amsterdam + + # EIP-7708 transfer log constants + system_address = Address(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE) + + # Create a simple transfer transaction + recipient = Address(0x100) + transfer_value = 1000 + + # Create intentionally wrong expected logs based on mismatch type + if mismatch_type == "address": + wrong_log = TransactionLog( + address=Address(0x1234), # Wrong address, should be system_address + ) + elif mismatch_type == "topics": + wrong_log = TransactionLog( + address=system_address, + topics=[Hash(b"\x00" * 32)], # Wrong topic + ) + else: # data + wrong_log = TransactionLog( + address=system_address, + data=Bytes((9999).to_bytes(32, "big")), # Wrong data + ) + + tx = Transaction( + secret_key=TestPrivateKey, + to=recipient, + value=transfer_value, + expected_receipt=TransactionReceipt(logs=[wrong_log]), + ) + + pre = Alloc({TestAddress: Account(balance=10**18)}) + + state_test = StateTest( + env=Environment(), + pre=pre, + post={}, # Empty post to skip post-state verification + tx=tx, + fork=Amsterdam, + ) + + with pytest.raises(LogMismatchError): + state_test.generate(t8n=default_t8n, fixture_format=StateFixture) diff --git a/packages/testing/src/execution_testing/test_types/__init__.py b/packages/testing/src/execution_testing/test_types/__init__.py index ef308059504..c87148f92a8 100644 --- a/packages/testing/src/execution_testing/test_types/__init__.py +++ b/packages/testing/src/execution_testing/test_types/__init__.py @@ -30,7 +30,7 @@ compute_deterministic_create2_address, ) from .phase_manager import TestPhase, TestPhaseManager -from .receipt_types import TransactionReceipt +from .receipt_types import TransactionLog, TransactionReceipt from .request_types import ( ConsolidationRequest, DepositRequest, @@ -77,6 +77,7 @@ "TestPhaseManager", "Transaction", "TransactionDefaults", + "TransactionLog", "TransactionReceipt", "TransactionTestMetadata", "TransactionType", diff --git a/packages/testing/src/execution_testing/test_types/receipt_types.py b/packages/testing/src/execution_testing/test_types/receipt_types.py index 66b1216daf4..dc36eb1a8af 100644 --- a/packages/testing/src/execution_testing/test_types/receipt_types.py +++ b/packages/testing/src/execution_testing/test_types/receipt_types.py @@ -17,15 +17,15 @@ class TransactionLog(CamelModel): """Transaction log.""" - address: Address - topics: List[Hash] - data: Bytes - block_number: HexNumber - transaction_hash: Hash - transaction_index: HexNumber - block_hash: Hash - log_index: HexNumber - removed: bool + address: Address | None = None + topics: List[Hash] | None = None + data: Bytes | None = None + block_number: HexNumber | None = None + transaction_hash: Hash | None = None + transaction_index: HexNumber | None = None + block_hash: Hash | None = None + log_index: HexNumber | None = None + removed: bool | None = None class ReceiptDelegation(CamelModel): diff --git a/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py b/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py index 544838a5dbb..17f4b8735d3 100644 --- a/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py +++ b/src/ethereum_spec_tools/evm_tools/t8n/t8n_types.py @@ -416,6 +416,17 @@ def json_encode_receipts(self) -> Any: receipt_dict["gasUsed"] = hex(receipt.cumulative_gas_used) receipt_dict["bloom"] = "0x" + receipt.bloom.hex() + # Add logs to receipts + logs_json = [] + for log in receipt.logs: + log_dict = { + "address": "0x" + log.address.hex(), + "topics": ["0x" + topic.hex() for topic in log.topics], + "data": "0x" + log.data.hex(), + } + logs_json.append(log_dict) + receipt_dict["logs"] = logs_json + receipts_json.append(receipt_dict) return receipts_json From 726ca61d3693e836012e87e3f6953f116a839e1b Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Fri, 16 Jan 2026 12:05:26 +0000 Subject: [PATCH 10/21] feat(test-tests): add eip-7708 eth transfer log tests --- .../eip7708_eth_transfer_logs/__init__.py | 1 + .../eip7708_eth_transfer_logs/spec.py | 33 +++ .../test_eth_transfer_logs.py | 253 ++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 tests/amsterdam/eip7708_eth_transfer_logs/__init__.py create mode 100644 tests/amsterdam/eip7708_eth_transfer_logs/spec.py create mode 100644 tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/__init__.py b/tests/amsterdam/eip7708_eth_transfer_logs/__init__.py new file mode 100644 index 00000000000..2f36477a667 --- /dev/null +++ b/tests/amsterdam/eip7708_eth_transfer_logs/__init__.py @@ -0,0 +1 @@ +"""Cross-client EIP-7708 Tests.""" diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py new file mode 100644 index 00000000000..d7fcd99cbd1 --- /dev/null +++ b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py @@ -0,0 +1,33 @@ +"""Defines EIP-7708 specification constants and functions.""" + +from dataclasses import dataclass + +from execution_testing import Address, Hash, keccak256 + + +@dataclass(frozen=True) +class ReferenceSpec: + """Defines the reference spec version and git path.""" + + git_path: str + version: str + + +ref_spec_7708 = ReferenceSpec( + "EIPS/eip-7708.md", "a7c5b2ff5697d5a0be5ea804a89d98a7fd0dce60" +) + + +@dataclass(frozen=True) +class Spec: + """ + Parameters from the EIP-7708 specifications as defined at + https://eips.ethereum.org/EIPS/eip-7708. + """ + + SYSTEM_ADDRESS: Address = Address( + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE + ) + TRANSFER_TOPIC: Hash = Hash( + keccak256(b"Transfer(address,address,uint256)") + ) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py new file mode 100644 index 00000000000..34c38084b17 --- /dev/null +++ b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py @@ -0,0 +1,253 @@ +""" +Tests [EIP-7708: ETH Transfers Emit a Log](https://eips.ethereum.org/EIPS/eip-7708). + +Tests for verifying that ETH transfers emit LOG3 events as specified. +""" + +import pytest +from execution_testing import ( + EOA, + Account, + Address, + Alloc, + Bytecode, + Bytes, + Environment, + Hash, + Op, + StateTestFiller, + Transaction, + TransactionLog, + TransactionReceipt, +) + +from .spec import Spec, ref_spec_7708 + +REFERENCE_SPEC_GIT_PATH = ref_spec_7708.git_path +REFERENCE_SPEC_VERSION = ref_spec_7708.version + +pytestmark = pytest.mark.valid_from("Amsterdam") + + +def transfer_log( + sender: Address, recipient: Address, amount: int +) -> TransactionLog: + """Create an expected transfer log.""" + return TransactionLog( + address=Spec.SYSTEM_ADDRESS, + topics=[ + Spec.TRANSFER_TOPIC, + Hash(bytes(sender).rjust(32, b"\x00")), + Hash(bytes(recipient).rjust(32, b"\x00")), + ], + data=Bytes(amount.to_bytes(32, "big")), + ) + + +def test_simple_transfer_emits_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, +) -> None: + """Test that a simple ETH transfer emits a transfer log.""" + recipient = pre.fund_eoa(0) + transfer_amount = 1000 + + tx = Transaction( + sender=sender, + to=recipient, + value=transfer_amount, + gas_limit=21_000, + expected_receipt=TransactionReceipt( + logs=[transfer_log(sender, recipient, transfer_amount)] + ), + ) + + post = {recipient: Account(balance=transfer_amount)} + state_test(env=env, pre=pre, post=post, tx=tx) + + +def test_zero_value_transfer_no_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, +) -> None: + """Test that a zero-value transfer does NOT emit a transfer log.""" + recipient = pre.fund_eoa(0) + + tx = Transaction( + sender=sender, + to=recipient, + value=0, + gas_limit=21_000, + expected_receipt=TransactionReceipt(logs=[]), + ) + + state_test(env=env, pre=pre, post={}, tx=tx) + + +def test_call_with_value_emits_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, +) -> None: + """Test that CALL with value emits a transfer log.""" + recipient = Address(0x200) + transfer_amount = 500 + tx_transfer_amount = 1000 + + contract_code = Op.CALL( + gas=100_000, + address=recipient, + value=transfer_amount, + ) + contract = pre.deploy_contract( + contract_code, balance=tx_transfer_amount + ) + + tx = Transaction( + sender=sender, + to=contract, + value=tx_transfer_amount, + gas_limit=100_000, + expected_receipt=TransactionReceipt( + logs=[ + transfer_log(sender, contract, tx_transfer_amount), + transfer_log(contract, recipient, transfer_amount), + ] + ), + ) + + post = {recipient: Account(balance=transfer_amount)} + state_test(env=env, pre=pre, post=post, tx=tx) + + +def test_selfdestruct_with_value_emits_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, +) -> None: + """Test that SELFDESTRUCT with value emits a transfer log.""" + beneficiary = Address(0x300) + contract_balance = 2000 + + contract_code = Op.SELFDESTRUCT(beneficiary) + contract = pre.deploy_contract(contract_code, balance=contract_balance) + + tx = Transaction( + sender=sender, + to=contract, + value=0, + gas_limit=100_000, + expected_receipt=TransactionReceipt( + logs=[transfer_log(contract, beneficiary, contract_balance)] + ), + ) + + post = {beneficiary: Account(balance=contract_balance)} + state_test(env=env, pre=pre, post=post, tx=tx) + + +@pytest.mark.parametrize( + "contract_code,contract_balance,expected_logs_empty", + [ + pytest.param( + Op.CALL(gas=100_000, address=Address(0x200), value=0), + 0, + True, + id="call_zero_value", + ), + pytest.param( + Op.SELFDESTRUCT(Address(0x300)), + 0, + True, + id="selfdestruct_zero_balance", + ), + ], +) +def test_zero_value_operations_no_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, + contract_code: Bytecode, + contract_balance: int, + expected_logs_empty: bool, +) -> None: + """Test that zero-value operations do NOT emit transfer logs.""" + contract = pre.deploy_contract(contract_code, balance=contract_balance) + + tx = Transaction( + sender=sender, + to=contract, + value=0, + gas_limit=100_000, + expected_receipt=TransactionReceipt(logs=[]), + ) + + state_test(env=env, pre=pre, post={}, tx=tx) + + +@pytest.mark.parametrize( + "recipient_code,call_gas", + [ + pytest.param(Op.REVERT(0, 0), 50_000, id="reverted_call"), + pytest.param(Op.JUMP(0), 100, id="out_of_gas_call"), + ], +) +def test_failed_call_no_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, + recipient_code: Bytecode, + call_gas: int, +) -> None: + """Test that failed inner CALLs do NOT emit transfer logs.""" + recipient = pre.deploy_contract(recipient_code) + call_value = 500 + tx_value = 1000 + + contract_code = Op.CALL( + gas=call_gas, + address=recipient, + value=call_value, + ) + contract = pre.deploy_contract(contract_code, balance=call_value) + + tx = Transaction( + sender=sender, + to=contract, + value=tx_value, + gas_limit=100_000, + expected_receipt=TransactionReceipt( + logs=[transfer_log(sender, contract, tx_value)] + ), + ) + + state_test(env=env, pre=pre, post={}, tx=tx) + + +def test_reverted_transaction_no_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, +) -> None: + """Test that a reverted transaction does NOT emit a transfer log.""" + contract_code = Op.REVERT(0, 0) + contract = pre.deploy_contract(contract_code) + + tx = Transaction( + sender=sender, + to=contract, + value=1000, + gas_limit=100_000, + expected_receipt=TransactionReceipt(logs=[]), + ) + + state_test(env=env, pre=pre, post={}, tx=tx) From 87e76ef5acba285339778d4cd3b633cdbfecffb9 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Tue, 20 Jan 2026 13:39:05 +0000 Subject: [PATCH 11/21] feat(spec-specs): add selfdestruct event topic and logging function --- src/ethereum/forks/amsterdam/vm/__init__.py | 35 +++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index 7f7f699112e..8d52b2adaa1 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -32,6 +32,7 @@ __all__ = ("Environment", "Evm", "Message") TRANSFER_TOPIC = keccak256(b"Transfer(address,address,uint256)") +SELFDESTRUCT_TOPIC = keccak256(b"Selfdestruct(address, uint256)") SYSTEM_ADDRESS = Address( bytes.fromhex("fffffffffffffffffffffffffffffffffffffffe") ) @@ -247,3 +248,37 @@ def emit_transfer_log( ) evm.logs = evm.logs + (log_entry,) + + +def emit_selfdestruct_log( + evm: Evm, + account: Address, + amount: U256, +) -> None: + """ + Emit a LOG2 for self-destruct to self (balance burn) per EIP-7708. + + Parameters + ---------- + evm : + The state of the ethereum virtual machine + account : + The account address being selfdestructed + amount : + The amount of ETH being destroyed + + """ + if amount == 0: + return + + padded_account = left_pad_zero_bytes(account, 32) + log_entry = Log( + address=SYSTEM_ADDRESS, + topics=( + SELFDESTRUCT_TOPIC, + Hash32(padded_account), + ), + data=amount.to_be_bytes32(), + ) + + evm.logs = evm.logs + (log_entry,) From e1aa252825442d8bcb5e97ebbd4b3babe23adc91 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Tue, 20 Jan 2026 13:39:35 +0000 Subject: [PATCH 12/21] feat(spec-specs): selfdestruct to self emits selfdestruct event --- .../forks/amsterdam/vm/instructions/system.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ethereum/forks/amsterdam/vm/instructions/system.py b/src/ethereum/forks/amsterdam/vm/instructions/system.py index d20217e9839..350ca7b7ccb 100644 --- a/src/ethereum/forks/amsterdam/vm/instructions/system.py +++ b/src/ethereum/forks/amsterdam/vm/instructions/system.py @@ -44,6 +44,7 @@ from .. import ( Evm, Message, + emit_selfdestruct_log, emit_transfer_log, incorporate_child_on_error, incorporate_child_on_success, @@ -684,9 +685,13 @@ def selfdestruct(evm: Evm) -> None: beneficiary_new_balance, ) - emit_transfer_log( - evm, evm.message.current_target, beneficiary, originator_balance - ) + # EIP-7708: Emit appropriate log based on beneficiary + if beneficiary == originator: + # Self-destruct to self burns the balance + emit_selfdestruct_log(evm, originator, originator_balance) + else: + # Transfer to different beneficiary + emit_transfer_log(evm, originator, beneficiary, originator_balance) # register account for deletion only if it was created # in the same transaction From bd6815cf4c221c462a23a94c3e43fdc9a5900e11 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Tue, 20 Jan 2026 13:41:12 +0000 Subject: [PATCH 13/21] feat(spec-specs): define call success constant --- src/ethereum/forks/amsterdam/vm/__init__.py | 1 + src/ethereum/forks/amsterdam/vm/instructions/system.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index 8d52b2adaa1..1bc55f9513a 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -36,6 +36,7 @@ SYSTEM_ADDRESS = Address( bytes.fromhex("fffffffffffffffffffffffffffffffffffffffe") ) +CALL_SUCCESS = U256(1) @dataclass diff --git a/src/ethereum/forks/amsterdam/vm/instructions/system.py b/src/ethereum/forks/amsterdam/vm/instructions/system.py index 350ca7b7ccb..e01532a1934 100644 --- a/src/ethereum/forks/amsterdam/vm/instructions/system.py +++ b/src/ethereum/forks/amsterdam/vm/instructions/system.py @@ -42,6 +42,7 @@ calculate_delegation_cost, ) from .. import ( + CALL_SUCCESS, Evm, Message, emit_selfdestruct_log, @@ -376,7 +377,7 @@ def generic_call( else: incorporate_child_on_success(evm, child_evm) evm.return_data = child_evm.output - push(evm.stack, U256(1)) + push(evm.stack, CALL_SUCCESS) actual_output_size = min(memory_output_size, U256(len(child_evm.output))) memory_write( From a90aeb59befef79e2986bda14fbfad7f29a29dab Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Tue, 20 Jan 2026 13:44:51 +0000 Subject: [PATCH 14/21] test(test-tests): add selfdestruct topic and use empty account --- .../eip7708_eth_transfer_logs/spec.py | 3 + .../test_eth_transfer_logs.py | 56 ++++++++++--------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py index d7fcd99cbd1..a1fe4412a7d 100644 --- a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py +++ b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py @@ -31,3 +31,6 @@ class Spec: TRANSFER_TOPIC: Hash = Hash( keccak256(b"Transfer(address,address,uint256)") ) + SELFDESTRUCT_TOPIC: Hash = Hash( + keccak256(b"Selfdestruct(address,uint256)") + ) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py index 34c38084b17..8732f2d86b7 100644 --- a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py +++ b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py @@ -51,7 +51,7 @@ def test_simple_transfer_emits_log( sender: EOA, ) -> None: """Test that a simple ETH transfer emits a transfer log.""" - recipient = pre.fund_eoa(0) + recipient = pre.empty_account() transfer_amount = 1000 tx = Transaction( @@ -75,7 +75,7 @@ def test_zero_value_transfer_no_log( sender: EOA, ) -> None: """Test that a zero-value transfer does NOT emit a transfer log.""" - recipient = pre.fund_eoa(0) + recipient = pre.empty_account() tx = Transaction( sender=sender, @@ -95,7 +95,7 @@ def test_call_with_value_emits_log( sender: EOA, ) -> None: """Test that CALL with value emits a transfer log.""" - recipient = Address(0x200) + recipient = pre.empty_account() transfer_amount = 500 tx_transfer_amount = 1000 @@ -104,9 +104,7 @@ def test_call_with_value_emits_log( address=recipient, value=transfer_amount, ) - contract = pre.deploy_contract( - contract_code, balance=tx_transfer_amount - ) + contract = pre.deploy_contract(contract_code, balance=tx_transfer_amount) tx = Transaction( sender=sender, @@ -132,7 +130,7 @@ def test_selfdestruct_with_value_emits_log( sender: EOA, ) -> None: """Test that SELFDESTRUCT with value emits a transfer log.""" - beneficiary = Address(0x300) + beneficiary = pre.empty_account() contract_balance = 2000 contract_code = Op.SELFDESTRUCT(beneficiary) @@ -153,20 +151,10 @@ def test_selfdestruct_with_value_emits_log( @pytest.mark.parametrize( - "contract_code,contract_balance,expected_logs_empty", + "op_type", [ - pytest.param( - Op.CALL(gas=100_000, address=Address(0x200), value=0), - 0, - True, - id="call_zero_value", - ), - pytest.param( - Op.SELFDESTRUCT(Address(0x300)), - 0, - True, - id="selfdestruct_zero_balance", - ), + pytest.param("call", id="call_zero_value"), + pytest.param("selfdestruct", id="selfdestruct_zero_balance"), ], ) def test_zero_value_operations_no_log( @@ -174,12 +162,17 @@ def test_zero_value_operations_no_log( env: Environment, pre: Alloc, sender: EOA, - contract_code: Bytecode, - contract_balance: int, - expected_logs_empty: bool, + op_type: str, ) -> None: """Test that zero-value operations do NOT emit transfer logs.""" - contract = pre.deploy_contract(contract_code, balance=contract_balance) + target = pre.empty_account() + + if op_type == "call": + contract_code = Op.CALL(gas=100_000, address=target, value=0) + else: + contract_code = Op.SELFDESTRUCT(target) + + contract = pre.deploy_contract(contract_code, balance=0) tx = Transaction( sender=sender, @@ -232,15 +225,24 @@ def test_failed_call_no_log( state_test(env=env, pre=pre, post={}, tx=tx) +@pytest.mark.parametrize( + "reverting_code", + [ + pytest.param(Op.REVERT(0, 0), id="revert"), + pytest.param(Op.INVALID, id="invalid_opcode"), + pytest.param(Op.ADD, id="stack_underflow"), + pytest.param(Op.MSTORE(2**256 - 1, 0), id="out_of_gas"), + ], +) def test_reverted_transaction_no_log( state_test: StateTestFiller, env: Environment, pre: Alloc, sender: EOA, + reverting_code: Bytecode, ) -> None: - """Test that a reverted transaction does NOT emit a transfer log.""" - contract_code = Op.REVERT(0, 0) - contract = pre.deploy_contract(contract_code) + """Test that a failed transaction does NOT emit a transfer log.""" + contract = pre.deploy_contract(reverting_code) tx = Transaction( sender=sender, From 86a7b1daccd1206e684c6b7343d7ade639d98395 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Tue, 20 Jan 2026 16:17:01 +0000 Subject: [PATCH 15/21] test(test-tests): add nested calls log ordering test --- .../test_eth_transfer_logs.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py index 8732f2d86b7..009daf34ad8 100644 --- a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py +++ b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py @@ -225,6 +225,65 @@ def test_failed_call_no_log( state_test(env=env, pre=pre, post={}, tx=tx) +@pytest.mark.parametrize( + "call_depth", + [ + pytest.param(2, id="depth_2"), + pytest.param(3, id="depth_3"), + pytest.param(10, id="depth_10"), + ], +) +def test_nested_calls_log_order( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, + call_depth: int, +) -> None: + """Test that nested CALLs emit transfer logs in chronological order.""" + transfer_value = 100 + tx_value = 1000 + + # Build chain of contracts: each calls the next with value + # contracts[0] -> contracts[1] -> ... -> contracts[depth-1] -> final_recipient + final_recipient = pre.empty_account() + contracts: list[Address] = [] + expected_logs: list[TransactionLog] = [] + + # Build contracts in reverse order (deepest first) + next_target = final_recipient + for i in range(call_depth): + contract_code = Op.CALL(gas=500_000, address=next_target, value=transfer_value) + # Each contract needs enough balance for its transfer + contract = pre.deploy_contract(contract_code, balance=transfer_value) + contracts.insert(0, contract) + next_target = contract + + # First contract is the tx target + entry_contract = contracts[0] + + # Build expected logs in chronological order + # First: tx-level transfer (sender -> entry_contract) + expected_logs.append(transfer_log(sender, entry_contract, tx_value)) + + # Then: each CALL in order + for i in range(call_depth): + from_addr = contracts[i] + to_addr = contracts[i + 1] if i + 1 < call_depth else final_recipient + expected_logs.append(transfer_log(from_addr, to_addr, transfer_value)) + + tx = Transaction( + sender=sender, + to=entry_contract, + value=tx_value, + gas_limit=1_000_000, + expected_receipt=TransactionReceipt(logs=expected_logs), + ) + + post = {final_recipient: Account(balance=transfer_value)} + state_test(env=env, pre=pre, post=post, tx=tx) + + @pytest.mark.parametrize( "reverting_code", [ From 464bd01e5da703a1653d98c1bb77f8656797c51e Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Wed, 21 Jan 2026 13:47:07 +0000 Subject: [PATCH 16/21] feat(test-tests): add selfdestruct finalization test --- .../test_eth_transfer_logs.py | 192 ++++++++++++++++-- 1 file changed, 179 insertions(+), 13 deletions(-) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py index 009daf34ad8..9afddddc167 100644 --- a/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py +++ b/tests/amsterdam/eip7708_eth_transfer_logs/test_eth_transfer_logs.py @@ -19,6 +19,7 @@ Transaction, TransactionLog, TransactionReceipt, + compute_create_address, ) from .spec import Spec, ref_spec_7708 @@ -150,11 +151,95 @@ def test_selfdestruct_with_value_emits_log( state_test(env=env, pre=pre, post=post, tx=tx) +def selfdestruct_log(contract: Address, amount: int) -> TransactionLog: + """Create an expected selfdestruct log (for selfdestruct to self).""" + return TransactionLog( + address=Spec.SYSTEM_ADDRESS, + topics=[ + Spec.SELFDESTRUCT_TOPIC, + Hash(bytes(contract).rjust(32, b"\x00")), + ], + data=Bytes(amount.to_bytes(32, "big")), + ) + + +def test_selfdestruct_to_self_emits_finalization_log( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, +) -> None: + """ + Test that selfdestruct-to-self emits a finalization log for remaining ETH. + + Scenario: + 1. Factory creates child contract via CREATE with 1000 wei + 2. Factory calls child, child selfdestructs to itself (emits log for 1000) + 3. Factory sends 500 more wei to child (transfer log emitted) + 4. At finalization, child has 500 wei remaining (emits finalization log) + + This tests the EIP-7708 requirement that when a contract receives ETH after + being flagged for SELFDESTRUCT, a Selfdestruct log is emitted at + finalization for the remaining balance. + """ + tx_value = 2000 + child_init_balance = 1000 + additional_eth = 500 + + # Child contract: selfdestructs to itself (address(this)) + child_code = Op.SELFDESTRUCT(Op.ADDRESS) + child_initcode = Op.MSTORE( + 0, Op.PUSH32(bytes(child_code).rjust(32, b"\x00")) + ) + Op.RETURN(32 - len(child_code), len(child_code)) + + initcode_len = len(child_initcode) + + # Factory: CREATE child, CALL to trigger selfdestruct, CALL again with ETH + factory_code = ( + Op.CALLDATACOPY(0, 0, Op.CALLDATASIZE) + + Op.SSTORE(1, Op.CREATE(child_init_balance, 0, initcode_len)) + + Op.CALL(address=Op.SLOAD(1), value=0, gas=50_000) + + Op.POP + + Op.CALL(address=Op.SLOAD(1), value=additional_eth, gas=50_000) + + Op.POP + ) + + factory = pre.deploy_contract( + factory_code, balance=child_init_balance + additional_eth + ) + child_addr = compute_create_address(address=factory, nonce=1) + + # Expected logs: + # 1. Transfer: sender -> factory (tx value) + # 2. Transfer: factory -> child (CREATE value) + # 3. Selfdestruct: child (initial balance at execution) + # 4. Transfer: factory -> child (additional ETH) + # 5. Selfdestruct: child (remaining balance at finalization) + expected_logs = [ + transfer_log(sender, factory, tx_value), + transfer_log(factory, child_addr, child_init_balance), + selfdestruct_log(child_addr, child_init_balance), + transfer_log(factory, child_addr, additional_eth), + selfdestruct_log(child_addr, additional_eth), + ] + + tx = Transaction( + sender=sender, + to=factory, + value=tx_value, + gas_limit=500_000, + data=bytes(child_initcode), + expected_receipt=TransactionReceipt(logs=expected_logs), + ) + + state_test(env=env, pre=pre, post={}, tx=tx) + + @pytest.mark.parametrize( "op_type", [ - pytest.param("call", id="call_zero_value"), - pytest.param("selfdestruct", id="selfdestruct_zero_balance"), + pytest.param("call", id="call"), + pytest.param("selfdestruct", id="selfdestruct"), ], ) def test_zero_value_operations_no_log( @@ -186,23 +271,31 @@ def test_zero_value_operations_no_log( @pytest.mark.parametrize( - "recipient_code,call_gas", + "recipient_code,call_gas,call_value,recipient_balance", [ - pytest.param(Op.REVERT(0, 0), 50_000, id="reverted_call"), - pytest.param(Op.JUMP(0), 100, id="out_of_gas_call"), + pytest.param(Op.REVERT(0, 0), 50_000, 500, 0, id="call_reverted"), + pytest.param(Op.JUMP(0), 100, 500, 0, id="call_out_of_gas"), + pytest.param( + Op.SELFDESTRUCT(Address(0x1234)), + 100, + 0, + 2000, + id="selfdestruct_out_of_gas", + ), ], ) -def test_failed_call_no_log( +def test_failed_inner_operation_no_log( state_test: StateTestFiller, env: Environment, pre: Alloc, sender: EOA, recipient_code: Bytecode, call_gas: int, + call_value: int, + recipient_balance: int, ) -> None: - """Test that failed inner CALLs do NOT emit transfer logs.""" - recipient = pre.deploy_contract(recipient_code) - call_value = 500 + """Test that failed inner operations do NOT emit transfer logs.""" + recipient = pre.deploy_contract(recipient_code, balance=recipient_balance) tx_value = 1000 contract_code = Op.CALL( @@ -244,16 +337,17 @@ def test_nested_calls_log_order( transfer_value = 100 tx_value = 1000 - # Build chain of contracts: each calls the next with value - # contracts[0] -> contracts[1] -> ... -> contracts[depth-1] -> final_recipient + # Build chain: contracts[0] -> contracts[1] -> ... -> final_recipient final_recipient = pre.empty_account() contracts: list[Address] = [] expected_logs: list[TransactionLog] = [] # Build contracts in reverse order (deepest first) next_target = final_recipient - for i in range(call_depth): - contract_code = Op.CALL(gas=500_000, address=next_target, value=transfer_value) + for _ in range(call_depth): + contract_code = Op.CALL( + gas=500_000, address=next_target, value=transfer_value + ) # Each contract needs enough balance for its transfer contract = pre.deploy_contract(contract_code, balance=transfer_value) contracts.insert(0, contract) @@ -312,3 +406,75 @@ def test_reverted_transaction_no_log( ) state_test(env=env, pre=pre, post={}, tx=tx) + + +@pytest.mark.parametrize( + "address_type", + [ + pytest.param("ecrecover", id="precompile_ecrecover"), + pytest.param("sha256", id="precompile_sha256"), + pytest.param("system", id="system_address"), + pytest.param("coinbase", id="coinbase_address"), + ], +) +def test_transfer_to_special_address( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, + address_type: str, +) -> None: + """Test that transfers to special addresses emit transfer logs.""" + transfer_amount = 1000 + + # Resolve target address based on type + # Note: blake2f (0x09) excluded as it requires specific input format + address_map = { + "ecrecover": Address(0x01), + "sha256": Address(0x02), + "system": Spec.SYSTEM_ADDRESS, + } + + if address_type == "coinbase": + target = env.fee_recipient + # Don't check exact balance - coinbase also receives gas fees + post = {} + else: + target = address_map[address_type] + post = {target: Account(balance=transfer_amount)} + + tx = Transaction( + sender=sender, + to=target, + value=transfer_amount, + gas_limit=100_000, + expected_receipt=TransactionReceipt( + logs=[transfer_log(sender, target, transfer_amount)] + ), + ) + + state_test(env=env, pre=pre, post=post, tx=tx) + + +@pytest.mark.with_all_typed_transactions +def test_transfer_with_all_tx_types( + state_test: StateTestFiller, + env: Environment, + pre: Alloc, + sender: EOA, + typed_transaction: Transaction, +) -> None: + """Test that ETH transfers emit logs for all transaction types.""" + recipient = pre.empty_account() + transfer_amount = 1000 + + tx = typed_transaction.copy( + to=recipient, + value=transfer_amount, + expected_receipt=TransactionReceipt( + logs=[transfer_log(sender, recipient, transfer_amount)] + ), + ) + + post = {recipient: Account(balance=transfer_amount)} + state_test(env=env, pre=pre, post=post, tx=tx) From e43a134b59c0554cdf45c6dc19c3e29811929a8c Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Wed, 21 Jan 2026 14:17:34 +0000 Subject: [PATCH 17/21] feat(spec-specs): emit selfdestruct finalization log for remaining balance --- src/ethereum/forks/amsterdam/fork.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/ethereum/forks/amsterdam/fork.py b/src/ethereum/forks/amsterdam/fork.py index 3e45c3e9531..803a77a15ac 100644 --- a/src/ethereum/forks/amsterdam/fork.py +++ b/src/ethereum/forks/amsterdam/fork.py @@ -27,6 +27,7 @@ InvalidSenderError, NonceMismatchError, ) +from ethereum.utils.byte import left_pad_zero_bytes from . import vm from .block_access_lists.builder import build_block_access_list @@ -1074,8 +1075,29 @@ def process_transaction( block_output.block_gas_used += tx_gas_used_after_refund block_output.blob_gas_used += tx_blob_gas_used + # EIP-7708: Emit selfdestruct logs for remaining balance at finalization. + # This handles the case where a contract receives ETH after being flagged + # for SELFDESTRUCT but before finalization. + finalization_logs: List[Log] = [] + for address in tx_output.accounts_to_delete: + balance = get_account(block_env.state, address).balance + if balance > U256(0): + padded_address = left_pad_zero_bytes(address, 32) + finalization_logs.append( + Log( + address=vm.SYSTEM_ADDRESS, + topics=( + vm.SELFDESTRUCT_TOPIC, + Hash32(padded_address), + ), + data=balance.to_be_bytes32(), + ) + ) + + all_logs = tx_output.logs + tuple(finalization_logs) + receipt = make_receipt( - tx, tx_output.error, block_output.block_gas_used, tx_output.logs + tx, tx_output.error, block_output.block_gas_used, all_logs ) receipt_key = rlp.encode(Uint(index)) @@ -1087,7 +1109,7 @@ def process_transaction( receipt, ) - block_output.block_logs += tx_output.logs + block_output.block_logs += all_logs for address in tx_output.accounts_to_delete: destroy_account(block_env.state, address) From 0bef4c961c2a76e9572ce21236db4a3944f72474 Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Wed, 21 Jan 2026 14:24:09 +0000 Subject: [PATCH 18/21] fix(test-tests): use spaces in event signature to match spec --- tests/amsterdam/eip7708_eth_transfer_logs/spec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py index a1fe4412a7d..984605a567e 100644 --- a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py +++ b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py @@ -29,8 +29,8 @@ class Spec: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE ) TRANSFER_TOPIC: Hash = Hash( - keccak256(b"Transfer(address,address,uint256)") + keccak256(b"Transfer(address, address, uint256)") ) SELFDESTRUCT_TOPIC: Hash = Hash( - keccak256(b"Selfdestruct(address,uint256)") + keccak256(b"Selfdestruct(address, uint256)") ) From f451d62f2cec20a4fc099d48e6cd90cab1ec05da Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Wed, 21 Jan 2026 21:23:44 -0500 Subject: [PATCH 19/21] fix(spec-specs): Refactor topic strings to match EIP --- src/ethereum/forks/amsterdam/vm/__init__.py | 2 +- tests/amsterdam/eip7708_eth_transfer_logs/spec.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index 1bc55f9513a..9bb27c9e486 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -32,7 +32,7 @@ __all__ = ("Environment", "Evm", "Message") TRANSFER_TOPIC = keccak256(b"Transfer(address,address,uint256)") -SELFDESTRUCT_TOPIC = keccak256(b"Selfdestruct(address, uint256)") +SELFDESTRUCT_TOPIC = keccak256(b"Selfdestruct(address,uint256)") SYSTEM_ADDRESS = Address( bytes.fromhex("fffffffffffffffffffffffffffffffffffffffe") ) diff --git a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py index 984605a567e..a1fe4412a7d 100644 --- a/tests/amsterdam/eip7708_eth_transfer_logs/spec.py +++ b/tests/amsterdam/eip7708_eth_transfer_logs/spec.py @@ -29,8 +29,8 @@ class Spec: 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE ) TRANSFER_TOPIC: Hash = Hash( - keccak256(b"Transfer(address, address, uint256)") + keccak256(b"Transfer(address,address,uint256)") ) SELFDESTRUCT_TOPIC: Hash = Hash( - keccak256(b"Selfdestruct(address, uint256)") + keccak256(b"Selfdestruct(address,uint256)") ) From b00840936c8dd7e6e546bd85f40c48c51af652a1 Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Wed, 21 Jan 2026 23:56:14 -0500 Subject: [PATCH 20/21] fix(spec-specs): emit account closure logs in lexicographical order --- src/ethereum/forks/amsterdam/fork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ethereum/forks/amsterdam/fork.py b/src/ethereum/forks/amsterdam/fork.py index 803a77a15ac..fada02c6605 100644 --- a/src/ethereum/forks/amsterdam/fork.py +++ b/src/ethereum/forks/amsterdam/fork.py @@ -1079,7 +1079,7 @@ def process_transaction( # This handles the case where a contract receives ETH after being flagged # for SELFDESTRUCT but before finalization. finalization_logs: List[Log] = [] - for address in tx_output.accounts_to_delete: + for address in sorted(tx_output.accounts_to_delete): balance = get_account(block_env.state, address).balance if balance > U256(0): padded_address = left_pad_zero_bytes(address, 32) From bfa2cf03e7fbe24bba556b53c4e57f8e089005e9 Mon Sep 17 00:00:00 2001 From: carsons-eels Date: Thu, 22 Jan 2026 00:19:28 -0500 Subject: [PATCH 21/21] fix(spec-tests): formatting fixes so static checks pass --- packages/testing/src/execution_testing/specs/helpers.py | 2 +- .../testing/src/execution_testing/specs/tests/test_expect.py | 4 +++- src/ethereum/forks/amsterdam/vm/__init__.py | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/testing/src/execution_testing/specs/helpers.py b/packages/testing/src/execution_testing/specs/helpers.py index 1f99274f75e..2ececeac530 100644 --- a/packages/testing/src/execution_testing/specs/helpers.py +++ b/packages/testing/src/execution_testing/specs/helpers.py @@ -333,7 +333,7 @@ def verify_transaction_receipt( actual_value=len(actual_logs), ) for log_idx, (expected, actual) in enumerate( - zip(expected_logs, actual_logs) + zip(expected_logs, actual_logs, strict=True) ): verify_log(transaction_index, log_idx, expected, actual) # TODO: Add more fields as needed diff --git a/packages/testing/src/execution_testing/specs/tests/test_expect.py b/packages/testing/src/execution_testing/specs/tests/test_expect.py index f0d309e850e..a8d28a8a690 100644 --- a/packages/testing/src/execution_testing/specs/tests/test_expect.py +++ b/packages/testing/src/execution_testing/specs/tests/test_expect.py @@ -646,7 +646,9 @@ def test_log_mismatch_during_generation( default_t8n: TransitionTool, mismatch_type: str, ) -> None: - """Test that log mismatches raise LogMismatchError during test generation.""" + """ + Test that log mismatches raise LogMismatchError during test generation. + """ from execution_testing.forks import Amsterdam # EIP-7708 transfer log constants diff --git a/src/ethereum/forks/amsterdam/vm/__init__.py b/src/ethereum/forks/amsterdam/vm/__init__.py index 9bb27c9e486..1e7ca7910f7 100644 --- a/src/ethereum/forks/amsterdam/vm/__init__.py +++ b/src/ethereum/forks/amsterdam/vm/__init__.py @@ -227,8 +227,8 @@ def emit_transfer_log( The state of the ethereum virtual machine sender : The account address sending the transfer - recipient :ce to finalize sco - The address of the transfer recipient account + recipient : + The account address recieving the transfer transfer_amount : The amount of ETH transacted