Skip to content

feat(tests): add initcode context extra test for clz opcode #1831

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 3 commits into
base: main
Choose a base branch
from

Conversation

LouisTsai-Csie
Copy link
Collaborator

@LouisTsai-Csie LouisTsai-Csie commented Jul 1, 2025

🗒️ Description

In this test case, I add two extra cases for CLZ opcode:

  1. Using CLZ opcode in the initcode for contract creation transaction.
  2. Using CLZ in the initcode for create/create2 contract creation.

🔗 Related Issues or PRs

Issue #1795

✅ Checklist

  • All: Ran fast tox checks to avoid unnecessary CI fails, see also Code Standards and Enabling Pre-commit Checks:
    uvx --with=tox-uv tox -e lint,typecheck,spellcheck,markdownlint
  • All: PR title adheres to the repo standard - it will be used as the squash commit message and should start type(scope):.
  • All: Considered adding an entry to CHANGELOG.md.
  • All: Considered updating the online docs in the ./docs/ directory.
  • All: Set appropriate labels for the changes (only maintainers can apply labels).
  • Tests: Ran mkdocs serve locally and verified the auto-generated docs for new tests in the Test Case Reference are correctly formatted.
  • Tests: For PRs implementing a missed test case, update the post-mortem document to add an entry the list.
  • Ported Tests: All converted JSON/YML tests from ethereum/tests or tests/static have been assigned @ported_from marker.

@LouisTsai-Csie LouisTsai-Csie changed the title Clz initcode context feat(tests): add initcode context extra test for clz opcode Jul 1, 2025
@LouisTsai-Csie LouisTsai-Csie self-assigned this Jul 1, 2025
@LouisTsai-Csie LouisTsai-Csie added fork:osaka Osaka hardfork type:test Type: Add/refactor fw unit tests; no fw or el client test case changes labels Jul 1, 2025
@LouisTsai-Csie LouisTsai-Csie force-pushed the clz-initcode-context branch from d86af47 to 8029357 Compare July 2, 2025 09:05
@LouisTsai-Csie
Copy link
Collaborator Author

This is my understanding of contract creation: during deployment, the contract’s creation code is executed. This code can store the final contract bytecode in memory, as shown in the test_clz_initcode_context in this PR

I’ve split the creation code into two parts. The first part is the initcode, which stores the result of the CLZ operation into storage slot 0 during contract creation for testing purpose. The second part uses EXTCODECOPY to load the deployed contract bytecode into memory.

Based on this structure, I want to extend the test case to cover CREATE and CREATE2, using the same technique of separating the initcode.

This is my ideal test case, but I’m unsure why the initcode isn’t executing during contract creation as expected.

@pytest.mark.valid_from("Osaka")
@pytest.mark.parametrize("bits", [0, 1, 4, 8, 16])
@pytest.mark.parametrize("opcode", [Op.CREATE])
def test_clz_initcode_create(state_test: StateTestFiller, pre: Alloc, bits: int, opcode: Op):
    """Test CLZ opcode behavior when creating a contract."""
    init_code = Op.SSTORE(0, Op.CLZ(1 << bits)) + Op.RETURN(0, Op.MSIZE)

    # init code is executed first, so the storage is set to the CLZ result
    creation_code = Op.SSTORE(1, Op.CLZ(1 << bits)) + Op.MSTORE(0, init_code.hex())

    sender_address = pre.fund_eoa()

    # factory contract will create a new contract
    if opcode == Op.CREATE:
        factory_code = creation_code + Op.CREATE(offset=0, size=len(init_code))

    factory_address = pre.deploy_contract(code=factory_code)

    # the new contract address is computed from the factory contract address and the nonce
    if opcode == Op.CREATE:
        contract_address = compute_create_address(address=factory_address, nonce=1)

    tx = Transaction(
        to=factory_address,
        gas_limit=6_000_000,
        data=creation_code,
        sender=sender_address,
    )

    expected_clz = 255 - bits

    # During the creation, the storage is set to the CLZ result based on the init code
    post = {
        contract_address: Account(storage={"0x00": expected_clz}),
    }

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

I am not sure if I got something wrong with create/create2, any recommendation is appreciated!

@jsign
Copy link
Collaborator

jsign commented Jul 3, 2025

@LouisTsai-Csie, I suspect your problem might be here Op.MSTORE(0, init_code.hex()).

init_code.hex() is less than 32-bytes, so MSTORE will left-pad with 0x00s. When you later do the CREATE copying len(init_code) those zeros will be first.

You prob want to try with padded_init_code = init_code + b"\x00" * (32 - init_code_size) and use padded_init_code for your MSTORE. Another option is to use the proper size in the CREATE to point to the non-zero required offset (and avoid the padding).

@LouisTsai-Csie LouisTsai-Csie marked this pull request as ready for review July 4, 2025 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fork:osaka Osaka hardfork type:test Type: Add/refactor fw unit tests; no fw or el client test case changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants