Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
7b9fc7d
feat(ci): Create Devnet Workflows (#2053)
marioevz Jan 21, 2026
f1ba2b1
feat(test): port static context static tests to python (#1960)
fselmo Jan 21, 2026
3b9b018
feat(tests): turn on EIP-7934 tests with BALs (fill all for amsterdam…
fselmo Jan 22, 2026
f98d725
feat(spec-specs): Add transfer log for all `CALL*` and `SELFDESTRUCT`
Carsons-Eels Jan 13, 2026
e82e5a3
fix(spec-specs): correct CR issues, fix formatting
Carsons-Eels Jan 16, 2026
cd2c014
fix(spec-specs): inline `execute_code()` to `process_message()`
Carsons-Eels Jan 20, 2026
7495813
chore(spec-specs): backport changes
Carsons-Eels Jan 21, 2026
9316b95
fix(spec-specs): trim out whitespace in topic hash to match tests
Carsons-Eels Jan 21, 2026
474371c
feat(test-specs): add transaction log verification support
spencer-tb Jan 16, 2026
726ca61
feat(test-tests): add eip-7708 eth transfer log tests
spencer-tb Jan 16, 2026
87e76ef
feat(spec-specs): add selfdestruct event topic and logging function
spencer-tb Jan 20, 2026
e1aa252
feat(spec-specs): selfdestruct to self emits selfdestruct event
spencer-tb Jan 20, 2026
bd6815c
feat(spec-specs): define call success constant
spencer-tb Jan 20, 2026
a90aeb5
test(test-tests): add selfdestruct topic and use empty account
spencer-tb Jan 20, 2026
86a7b1d
test(test-tests): add nested calls log ordering test
spencer-tb Jan 20, 2026
464bd01
feat(test-tests): add selfdestruct finalization test
spencer-tb Jan 21, 2026
e43a134
feat(spec-specs): emit selfdestruct finalization log for remaining ba…
spencer-tb Jan 21, 2026
0bef4c9
fix(test-tests): use spaces in event signature to match spec
spencer-tb Jan 21, 2026
f451d62
fix(spec-specs): Refactor topic strings to match EIP
Carsons-Eels Jan 22, 2026
b008409
fix(spec-specs): emit account closure logs in lexicographical order
Carsons-Eels Jan 22, 2026
bfa2cf0
fix(spec-tests): formatting fixes so static checks pass
Carsons-Eels Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .github/actions/merge-eip-branches/action.yaml
Original file line number Diff line number Diff line change
@@ -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}"
71 changes: 71 additions & 0 deletions .github/actions/rebase-eip-branch/action.yaml
Original file line number Diff line number Diff line change
@@ -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}"
3 changes: 1 addition & 2 deletions .github/configs/feature.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
36 changes: 36 additions & 0 deletions .github/workflows/eip-rebase.yaml
Original file line number Diff line number Diff line change
@@ -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 }}
44 changes: 44 additions & 0 deletions .github/workflows/update-devnet-branch.yaml
Original file line number Diff line number Diff line change
@@ -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 }}
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions packages/testing/src/execution_testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
TestParameterGroup,
TestPhaseManager,
Transaction,
TransactionLog,
TransactionReceipt,
TransactionType,
Withdrawal,
Expand Down Expand Up @@ -185,6 +186,7 @@
"TestPrivateKey2",
"Transaction",
"TransactionException",
"TransactionLog",
"TransactionReceipt",
"TransactionTest",
"TransactionTestFiller",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1123,14 +1123,64 @@ 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:
"""
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Loading
Loading