Skip to content

new(tests): port ethereum/tests calldataload opcode tests #1248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions converted-ethereum-tests.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
([#1056](https://github.com/ethereum/execution-spec-tests/pull/1056))
GeneralStateTests/VMTests/vmTests/calldatacopy.json

([#1248](https://github.com/ethereum/execution-spec-tests/pull/1248))
GeneralStateTests/VMTests/vmTests/calldataload.json

([#748](https://github.com/ethereum/execution-spec-tests/pull/748))
GeneralStateTests/stBadOpcode/badOpcodes.json
GeneralStateTests/stBugs/evmBytecode.json
Expand Down
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Test fixtures for use by clients are available for each release on the [Github r

- ✨ Add gas cost of delegation access in CALL opcode ([#1208](https://github.com/ethereum/execution-spec-tests/pull/1208)).
- ✨ Add EIP-7698 failed nonce and short data tests ([#1211](https://github.com/ethereum/execution-spec-tests/pull/1211)).
- ✨ Port [calldataload test](https://github.com/ethereum/tests/blob/2cd297403439b6eea6e0eb5c2fab2111cb210c6f/src/GeneralStateTestsFiller/VMTests/vmTests/calldataloadFiller.yml#L4) ([#1056](https://github.com/ethereum/execution-spec-tests/pull/1248)).

## [v4.0.0](https://github.com/ethereum/execution-spec-tests/releases/tag/v3.0.0) - 2025-02-14 - πŸ’•

Expand Down
145 changes: 145 additions & 0 deletions tests/frontier/opcodes/test_calldataload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"""
abstract: Test `CALLDATALOAD`
Test the `CALLDATALOAD` opcodes.

"""

import pytest

from ethereum_test_forks import Fork, Frontier, Homestead
from ethereum_test_tools import (
Account,
Alloc,
Bytecode,
Environment,
StateTestFiller,
Transaction,
)
from ethereum_test_tools import Opcodes as Op


@pytest.mark.parametrize(
"calldataload_offset,memory_preset_code,call_args_size,expected_calldataload_memory",
[
pytest.param(
0x00,
Op.MSTORE8(offset=0x0, value=0x25) + Op.MSTORE8(offset=0x1, value=0x60),
0x02,
0x2560000000000000000000000000000000000000000000000000000000000000,
id="calldata_short_start_0",
),
pytest.param(
0x01,
Op.MSTORE(
offset=0x0,
value=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,
)
+ Op.MSTORE8(offset=0x20, value=0x23),
0x21,
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF23,
id="calldata_sufficient_with_offset",
),
pytest.param(
0x05,
Op.MSTORE(
offset=0x0,
value=0x123456789ABCDEF0000000000000000000000000000000000000000000000000,
)
+ Op.MSTORE8(offset=0x20, value=0x0)
+ Op.MSTORE8(offset=0x21, value=0x24),
0x22,
0xBCDEF00000000000000000000000000000000000000000000000000024000000,
id="calldata_partial_word_at_offset",
),
],
)
@pytest.mark.valid_from("Frontier")
def test_calldataload(
state_test: StateTestFiller,
fork: Fork,
calldataload_offset: int,
memory_preset_code: Bytecode,
call_args_size: int,
expected_calldataload_memory: int,
pre: Alloc,
):
"""
Test `CALLDATACOPY` opcode.

This test verifies that `CALLDATALOAD` correctly retrieves a 32-byte word
from calldata at different offsets, handling various edge cases.

Parameter:

calldataload_offset: int
The offset that determines the memory reading position, controlling whether the test case
retrieves a partial or full 32-byte word from calldata.
memory_preset_code: Bytecode
Simulates different memory layouts to test how `CALLDATALOAD` behaves when calldata
is smaller or larger than 32 bytes.
call_args_size: int
Specifies the number of bytes from `memory_preset_code` that are actually forwarded
as calldata during execution.
expected_calldataload_memory: int
Validates whether `CALLDATALOAD` correctly retrieves and zero-pads data by comparing
the stored result in contract storage with this expected reference value.

Test Cases:

calldata_short_start_0:
- The calldata size is less than 32 bytes.
- `CALLDATALOAD` starts at offset 0x00.
- The result should correctly pad missing bytes with zeros.

calldata_sufficient_with_offset:
- The calldata size is greater than 32 bytes.
- `CALLDATALOAD` starts at a valid offset within calldata**.
- A full 32-byte word is retrieved successfully.

calldata_partial_word_at_offset:
- The calldata size is greater than 32 bytes but `CALLDATALOAD` starts at an offset
where `offset + 32` exceeds the calldata size.
- The result should return the available bytes, and the rest should be zero-padded.

Based on https://github.com/ethereum/tests/blob/develop/src/GeneralStateTestsFiller/VMTests/vmTests/calldataloadFiller.yml
"""
env = Environment()
sender = pre.fund_eoa()
post = {}

# Deploy the contract that will store the calldata
sstore_contract = pre.deploy_contract(
code=(Op.SSTORE(key=0x0, value=Op.CALLDATALOAD(offset=calldataload_offset)))
)

# Deploy the contract that will forward the calldata to the first contract
calldata_contract = pre.deploy_contract(
code=(
memory_preset_code
+ Op.CALL(
gas=Op.SUB(Op.GAS, 20000),
address=sstore_contract,
args_size=call_args_size,
)
)
)

validation_contract = pre.deploy_contract(
code=(
Op.CALL(
gas=Op.SUB(Op.GAS, 20000),
address=calldata_contract,
)
)
)

tx = Transaction(
gas_limit=100000,
protected=False if fork in [Frontier, Homestead] else True,
to=validation_contract,
sender=sender,
)

post[sstore_contract] = Account(storage={0x0: expected_calldataload_memory})

state_test(env=env, pre=pre, post=post, tx=tx)