Skip to content

new(tests): EIP-5656/7692 - use new marker to EOF-ize MCOPY test #726

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

Closed
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
76 changes: 29 additions & 47 deletions tests/cancun/eip5656_mcopy/test_mcopy_memory_expansion.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
that produce a memory expansion, and potentially an out-of-gas error.

""" # noqa: E501
from typing import Mapping, Tuple
from typing import Mapping

import pytest

from ethereum_test_tools import Account, Address, Alloc, Bytecode, Environment
from ethereum_test_tools import Opcodes as Op
from ethereum_test_tools import StateTestFiller, Storage, Transaction, cost_memory_bytes
from ethereum_test_tools import StateTestFiller, Transaction, cost_memory_bytes
from ethereum_test_types.helpers import eip_2028_transaction_data_cost

from .common import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION

Expand All @@ -31,6 +32,8 @@ def callee_bytecode(dest: int, src: int, length: int) -> Bytecode:
# Pushes for the return operation
bytecode += Op.PUSH1(0x00) + Op.PUSH1(0x00)

bytecode += Op.SSTORE(1, 1)

# Perform the mcopy operation
bytecode += Op.MCOPY(dest, src, length)

Expand All @@ -40,14 +43,16 @@ def callee_bytecode(dest: int, src: int, length: int) -> Bytecode:


@pytest.fixture
def subcall_exact_cost(
def call_exact_cost(
initial_memory: bytes,
dest: int,
length: int,
) -> int:
"""
Returns the exact cost of the subcall, based on the initial memory and the length of the copy.
"""
intrinsic_cost = 21000 + eip_2028_transaction_data_cost(initial_memory)

mcopy_cost = 3
mcopy_cost += 3 * ((length + 31) // 32)
if length > 0 and dest + length > len(initial_memory):
Expand All @@ -57,36 +62,18 @@ def subcall_exact_cost(
calldatacopy_cost += 3 * ((len(initial_memory) + 31) // 32)
calldatacopy_cost += cost_memory_bytes(len(initial_memory), 0)

pushes_cost = 3 * 7
pushes_cost = 3 * 9
calldatasize_cost = 2
return mcopy_cost + calldatacopy_cost + pushes_cost + calldatasize_cost


@pytest.fixture
def bytecode_storage(
subcall_exact_cost: int,
successful: bool,
memory_expansion_address: Address,
) -> Tuple[Bytecode, Storage.StorageDictType]:
"""
Prepares the bytecode and storage for the test, based on the expected result of the subcall
(whether it succeeds or fails depending on the length of the memory expansion).
"""
bytecode = Bytecode()
storage = {}

# Pass on the calldata
bytecode += Op.CALLDATACOPY(0x00, 0x00, Op.CALLDATASIZE())

subcall_gas = subcall_exact_cost if successful else subcall_exact_cost - 1

# Perform the subcall and store a one in the result location
bytecode += Op.SSTORE(
Op.CALL(subcall_gas, memory_expansion_address, 0, 0, Op.CALLDATASIZE(), 0, 0), 1
sstore_cost = 22100
return (
intrinsic_cost
+ mcopy_cost
+ calldatacopy_cost
+ pushes_cost
+ calldatasize_cost
+ sstore_cost
)
storage[int(successful)] = 1

return (bytecode, storage)


@pytest.fixture
Expand All @@ -101,10 +88,11 @@ def block_gas_limit() -> int: # noqa: D103

@pytest.fixture
def tx_gas_limit( # noqa: D103
subcall_exact_cost: int,
call_exact_cost: int,
block_gas_limit: int,
successful: bool,
) -> int:
return min(max(500_000, subcall_exact_cost * 2), block_gas_limit)
return min(call_exact_cost - (0 if successful else 1), block_gas_limit)


@pytest.fixture
Expand All @@ -115,14 +103,7 @@ def env( # noqa: D103


@pytest.fixture
def caller_address( # noqa: D103
pre: Alloc, bytecode_storage: Tuple[bytes, Storage.StorageDictType]
) -> Address:
return pre.deploy_contract(code=bytecode_storage[0])


@pytest.fixture
def memory_expansion_address(pre: Alloc, callee_bytecode: Bytecode) -> Address: # noqa: D103
def caller_address(pre: Alloc, callee_bytecode: bytes) -> Address: # noqa: D103
return pre.deploy_contract(code=callee_bytecode)


Expand Down Expand Up @@ -151,11 +132,10 @@ def tx( # noqa: D103

@pytest.fixture
def post( # noqa: D103
caller_address: Address, bytecode_storage: Tuple[bytes, Storage.StorageDictType]
caller_address: Address,
successful: bool,
) -> Mapping:
return {
caller_address: Account(storage=bytecode_storage[1]),
}
return {caller_address: Account(storage={1: 1} if successful else {})}


@pytest.mark.parametrize(
Expand Down Expand Up @@ -189,14 +169,15 @@ def post( # noqa: D103
@pytest.mark.parametrize(
"initial_memory",
[
bytes(range(0x00, 0x100)),
bytes(range(0x01, 0xFF)), # NOTE: must be non-zero bytes
bytes(),
],
ids=[
"from_existent_memory",
"from_empty_memory",
],
)
@pytest.mark.with_all_evm_code_types
@pytest.mark.valid_from("Cancun")
def test_mcopy_memory_expansion(
state_test: StateTestFiller,
Expand Down Expand Up @@ -242,22 +223,23 @@ def test_mcopy_memory_expansion(
],
)
@pytest.mark.parametrize(
"subcall_exact_cost",
"call_exact_cost",
[2**128 - 1],
ids=[""],
) # Limit subcall gas, otherwise it would be impossibly large
@pytest.mark.parametrize("successful", [False])
@pytest.mark.parametrize(
"initial_memory",
[
bytes(range(0x00, 0x100)),
bytes(range(0x01, 0xFF)), # NOTE: must be non-zero bytes
bytes(),
],
ids=[
"from_existent_memory",
"from_empty_memory",
],
)
@pytest.mark.with_all_evm_code_types
@pytest.mark.valid_from("Cancun")
def test_mcopy_huge_memory_expansion(
state_test: StateTestFiller,
Expand Down