Skip to content
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

feat(forks,tests): EIP-2935: Contract update #1046

Merged
merged 6 commits into from
Jan 10, 2025
Merged
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
5 changes: 3 additions & 2 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ [EIP-7623](https://eips.ethereum.org/EIPS/eip-7623) Increase calldata cost ([#1004](https://github.com/ethereum/execution-spec-tests/pull/1004))
- ✨ Add generic precompile-absence test ([#1036](https://github.com/ethereum/execution-spec-tests/pull/1036))
- ✨ Add test for [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537) which uses the full discount table of G2 MSM ([#1038](https://github.com/ethereum/execution-spec-tests/pull/1038))
- πŸ”€ Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
- πŸ”€ Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
- πŸ”€ Update EIP-2935 according to [spec updates](https://github.com/ethereum/EIPs/pull/9144) ([#1046](https://github.com/ethereum/execution-spec-tests/pull/1046))

### πŸ› οΈ Framework

Expand Down Expand Up @@ -67,8 +70,6 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Add a default location for evm logs (`--evm-dump-dir`) when filling tests ([#999](https://github.com/ethereum/execution-spec-tests/pull/999)).
- ✨ Slow tests now have greater timeout when making a request to the T8N server ([#1037](https://github.com/ethereum/execution-spec-tests/pull/1037)).
- ✨ Introduce [`pytest.mark.parametrize_by_fork`](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#pytestmarkfork_parametrize) helper marker ([#1019](https://github.com/ethereum/execution-spec-tests/pull/1019), [#1057](https://github.com/ethereum/execution-spec-tests/pull/1057)).
- πŸ”€ Update EIP-7251 according to [spec updates](https://github.com/ethereum/EIPs/pull/9127) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
- πŸ”€ Update EIP-7002 according to [spec updates](https://github.com/ethereum/EIPs/pull/9119) ([#1024](https://github.com/ethereum/execution-spec-tests/pull/1024)).
- 🐞 fix(consume): allow absolute paths with `--evm-bin` ([#1052](https://github.com/ethereum/execution-spec-tests/pull/1052)).

### πŸ”§ EVM Tools
Expand Down
Binary file modified src/ethereum_test_forks/forks/contracts/history_contract.bin
Binary file not shown.
4 changes: 2 additions & 2 deletions src/ethereum_test_forks/forks/forks.py
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ def system_contracts(cls, block_number: int = 0, timestamp: int = 0) -> List[Add
Address(0x00000000219AB540356CBB839CBE05303D7705FA),
Address(0x0C15F14308530B7CDB8460094BBB9CC28B9AAAAA),
Address(0x00431F263CE400F4455C2DCF564E53007CA4BBBB),
Address(0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E),
Address(0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC),
] + super(Prague, cls).system_contracts(block_number, timestamp)

@classmethod
Expand Down Expand Up @@ -1126,7 +1126,7 @@ def pre_allocation_blockchain(cls) -> Mapping:
with open(CURRENT_FOLDER / "contracts" / "history_contract.bin", mode="rb") as f:
new_allocation.update(
{
0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E: {
0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC: {
"nonce": 1,
"code": f.read(),
}
Expand Down
3 changes: 2 additions & 1 deletion src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
Yul,
YulCompiler,
)
from .utility.generators import generate_system_contract_deploy_test
from .utility.generators import DeploymentTestType, generate_system_contract_deploy_test
from .utility.pytest import extend_with_defaults

__all__ = (
Expand All @@ -108,6 +108,7 @@
"Conditional",
"ConsolidationRequest",
"DepositRequest",
"DeploymentTestType",
"EngineAPIError",
"Environment",
"EOFException",
Expand Down
55 changes: 27 additions & 28 deletions src/ethereum_test_tools/utility/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,24 @@
import json
from enum import Enum
from pathlib import Path
from typing import Dict, Generator, List, Protocol, Tuple
from typing import Dict, Generator, List, Protocol

import pytest

from ethereum_test_base_types import Account, Address
from ethereum_test_forks import Fork
from ethereum_test_specs import BlockchainTestFiller
from ethereum_test_specs.blockchain import Block, Header
from ethereum_test_specs.blockchain import Block
from ethereum_test_types import Alloc, Transaction


class DeploymentTestType(Enum):
"""Represents the type of deployment test."""

DEPLOY_BEFORE_FORK = "deploy_before_fork"
DEPLOY_AFTER_FORK = "deploy_after_fork"


class SystemContractDeployTestFunction(Protocol):
"""
Represents a function to be decorated with the `generate_system_contract_deploy_test`
Expand All @@ -25,33 +32,31 @@ def __call__(
*,
fork: Fork,
pre: Alloc,
) -> Generator[Tuple[Transaction | None, Header | None], None, None]:
post: Alloc,
test_type: DeploymentTestType,
) -> Generator[Block, None, None]:
"""
Args:
fork (Fork): The fork to test.
pre (Alloc): The pre state of the blockchain.
post (Alloc): The post state of the blockchain.
test_type (DeploymentTestType): The type of deployment test currently being filled.

Yields:
Tuple[Transaction | None, Header | None]: Once per block to add after the contract is
deployed, with a single transaction to execute and the header object used to
verify the block.
Block: To add after the block where the contract was deployed (e.g. can contain extra
transactions to execute after the system contract has been deployed, and/or a header
object to verify that the headers are correct).

"""
...


class DeploymentTestType(Enum):
"""Represents the type of deployment test."""

DEPLOY_BEFORE_FORK = "deploy_before_fork"
DEPLOY_AFTER_FORK = "deploy_after_fork"


def generate_system_contract_deploy_test(
*,
fork: Fork,
tx_json_path: Path,
expected_deploy_address: Address,
expected_system_contract_storage: Dict | None,
expected_system_contract_storage: Dict | None = None,
):
"""
Generate a test that verifies the correct deployment of a system contract.
Expand All @@ -66,7 +71,8 @@ def generate_system_contract_deploy_test(
contract.
Providing a JSON file is useful to copy-paste the transaction from the EIP.
expected_deploy_address (Address): The expected address of the deployed contract.
expected_system_contract_storage (Dict): The expected storage of the system contract.
expected_system_contract_storage (Dict | None): The expected storage of the system
contract.

"""
with open(tx_json_path, mode="r") as f:
Expand Down Expand Up @@ -126,18 +132,6 @@ def wrapper(
),
]

for tx_header_verify in func(fork=fork, pre=pre):
txs = []
if tx_header_verify[0] is not None:
txs.append(tx_header_verify[0])
header_verify = tx_header_verify[1]
blocks.append(
Block(
txs=txs,
header_verify=header_verify,
)
)

pre[expected_deploy_address] = Account(
code=b"", # Remove the code that is automatically allocated on the fork
nonce=0,
Expand All @@ -149,7 +143,7 @@ def wrapper(

expected_deploy_address_int = int.from_bytes(expected_deploy_address, "big")

post = {}
post = Alloc()
fork_pre_allocation = fork.pre_allocation_blockchain()
assert expected_deploy_address_int in fork_pre_allocation
expected_code = fork_pre_allocation[expected_deploy_address_int]["code"]
Expand All @@ -167,6 +161,11 @@ def wrapper(
post[deployer_address] = Account(
nonce=1,
)

# Extra blocks (if any) returned by the decorated function to add after the
# contract is deployed.
blocks += list(func(fork=fork, pre=pre, post=post, test_type=test_type))

blockchain_test(
pre=pre,
blocks=blocks,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "0x0",
"nonce": "0x0",
"to": null,
"gas": "0x3d090",
"gasPrice": "0xe8d4a51000",
"maxPriorityFeePerGas": null,
"maxFeePerGas": null,
"value": "0x0",
"input": "0x60538060095f395ff33373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500",
"v": "0x1b",
"r": "0x539",
"s": "0xbaefe09f0109759",
"hash": "0x8c7bd2d3713a0b2bb693463d2a78c4d612ac47dd38ecb74f8996a4b6fc96f03c"
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ReferenceSpec:
version: str


ref_spec_2935 = ReferenceSpec("EIPS/eip-2935.md", "68d54a80a4f5b9c0cf4ae3a10586d63ef221de36")
ref_spec_2935 = ReferenceSpec("EIPS/eip-2935.md", "a04da454a5a6ba86a87bb9e15f811feaff3c849a")


@dataclass(frozen=True)
Expand All @@ -22,6 +22,6 @@ class Spec:
"""

FORK_TIMESTAMP = 15_000
HISTORY_STORAGE_ADDRESS = 0x0AAE40965E6800CD9B1F4B05FF21581047E3F91E
HISTORY_SERVE_WINDOW = 8192
HISTORY_STORAGE_ADDRESS = 0x0F792BE4B0C0CB4DAE440EF133E90C0ECD48CCCC
HISTORY_SERVE_WINDOW = 8191
BLOCKHASH_OLD_WINDOW = 256
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,9 @@ def test_block_hashes_history(
@pytest.mark.parametrize(
"block_number,reverts",
[
pytest.param(1, False, id="current_block"),
pytest.param(2, False, id="future_block"),
pytest.param(2**64 - 1, False, id="2**64-1"),
pytest.param(1, True, id="current_block"),
pytest.param(2, True, id="future_block"),
pytest.param(2**64 - 1, True, id="2**64-1"),
pytest.param(2**64, True, id="2**64"),
],
)
Expand All @@ -337,13 +337,24 @@ def test_invalid_history_contract_calls(
returned_block_hash_slot = storage.store_next(0)
block_hash_opcode_slot = storage.store_next(0)

return_offset = 64
return_size = 32
args_size = 32

# Check the first block outside of the window if any
code = (
Op.MSTORE(0, block_number)
+ Op.SSTORE(
return_code_slot, Op.CALL(Op.GAS, Spec.HISTORY_STORAGE_ADDRESS, 0, 0, 32, 32, 64)
return_code_slot,
Op.CALL(
address=Spec.HISTORY_STORAGE_ADDRESS,
args_offset=0,
args_size=args_size,
ret_offset=return_offset,
ret_size=return_size,
),
)
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(32))
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset))
+ Op.SSTORE(block_hash_opcode_slot, Op.BLOCKHASH(block_number))
)
check_contract_address = pre.deploy_contract(code, storage=storage.canary())
Expand All @@ -364,3 +375,63 @@ def test_invalid_history_contract_calls(
post=post,
reverts=reverts,
)


@pytest.mark.parametrize(
"args_size,reverts",
[
pytest.param(0, True, id="zero_size"),
pytest.param(33, True, id="too_large"),
pytest.param(31, True, id="too_small"),
],
)
@pytest.mark.valid_from("Prague")
def test_invalid_history_contract_calls_input_size(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
reverts: bool,
args_size: int,
):
"""Test calling the history contract with invalid input sizes."""
storage = Storage()

return_code_slot = storage.store_next(not reverts, "history storage call result")
returned_block_hash_slot = storage.store_next(0)

return_offset = 64
return_size = 32
block_number = 0

# Check the first block outside of the window if any
code = (
Op.MSTORE(0, block_number)
+ Op.SSTORE(
return_code_slot,
Op.CALL(
address=Spec.HISTORY_STORAGE_ADDRESS,
args_offset=0,
args_size=args_size,
ret_offset=return_offset,
ret_size=return_size,
),
)
+ Op.SSTORE(returned_block_hash_slot, Op.MLOAD(return_offset))
)
check_contract_address = pre.deploy_contract(code, storage=storage.canary())

txs = [
Transaction(
to=check_contract_address,
gas_limit=10_000_000,
sender=pre.fund_eoa(),
)
]
post = {check_contract_address: Account(storage=storage)}

blocks = [Block(txs=txs)]
blockchain_test(
pre=pre,
blocks=blocks,
post=post,
reverts=reverts,
)
Loading
Loading