Skip to content

Commit 6df036c

Browse files
authored
feat(tests): port codeCopyZero and delete stExtCodeHash static tests (ethereum#2465)
Port the last remaining static test file codeCopyZero_ParisFiller.yml to two Python tests: - test_extcodecopy_zero_code: EXTCODECOPY/EXTCODESIZE/EXTCODEHASH of accounts with no code (nonexistent, funded EOA, empty EOA) - test_codecopy_zero_in_create2: CODECOPY inside CREATE2 initcode that deploys empty code, verifying initcode bytes are copied Delete all files in tests/static/state_tests/stExtCodeHash/ as all tests in that directory have now been ported.
1 parent d581f26 commit 6df036c

3 files changed

Lines changed: 161 additions & 127 deletions

File tree

tests/constantinople/eip1052_extcodehash/test_extcodehash.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,3 +1735,164 @@ def test_extcodehash_subcall_create2_oog(
17351735
)
17361736

17371737
state_test(pre=pre, post=post, tx=tx)
1738+
1739+
1740+
@pytest.mark.ported_from(
1741+
[
1742+
"https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stExtCodeHash/codeCopyZero_ParisFiller.yml", # noqa: E501
1743+
],
1744+
)
1745+
@pytest.mark.parametrize(
1746+
"target_type",
1747+
[
1748+
"nonexistent",
1749+
"existing",
1750+
],
1751+
)
1752+
def test_extcodecopy_zero_code(
1753+
state_test: StateTestFiller,
1754+
pre: Alloc,
1755+
target_type: str,
1756+
) -> None:
1757+
"""
1758+
Test EXTCODECOPY/EXTCODESIZE/EXTCODEHASH of accounts with no code.
1759+
1760+
Two account types: nonexistent (no state at all) and existing
1761+
(EOA with balance, no code). EXTCODECOPY writes nothing to memory
1762+
(stays zero). EXTCODEHASH is zero for nonexistent, keccak256("")
1763+
for existing accounts.
1764+
1765+
TODO: The original test also intended to cover empty accounts
1766+
(zero nonce, zero balance, no code), but such accounts cannot
1767+
exist in post-Paris forks due to EIP-161 cleanup.
1768+
"""
1769+
storage = Storage()
1770+
1771+
if target_type == "nonexistent":
1772+
target = pre.nonexistent_account()
1773+
expected_hash: int | bytes = 0
1774+
else: # existing
1775+
target = pre.fund_eoa(amount=1)
1776+
expected_hash = keccak256(b"")
1777+
1778+
code = (
1779+
Op.EXTCODECOPY(target, 0, 0, 32)
1780+
+ Op.SSTORE(storage.store_next(0, "extcodecopy"), Op.MLOAD(0))
1781+
+ Op.SSTORE(
1782+
storage.store_next(0, "extcodesize"),
1783+
Op.EXTCODESIZE(target),
1784+
)
1785+
+ Op.SSTORE(
1786+
storage.store_next(expected_hash, "extcodehash"),
1787+
Op.EXTCODEHASH(target),
1788+
)
1789+
+ Op.SSTORE(
1790+
storage.store_next(1, "callcode_result"),
1791+
Op.CALLCODE(50_000, target, 0, 0, 0, 0, 0),
1792+
)
1793+
)
1794+
1795+
code_address = pre.deploy_contract(code, storage=storage.canary())
1796+
1797+
tx = Transaction(
1798+
sender=pre.fund_eoa(),
1799+
to=code_address,
1800+
gas_limit=400_000,
1801+
)
1802+
1803+
state_test(
1804+
pre=pre,
1805+
post={code_address: Account(storage=storage)},
1806+
tx=tx,
1807+
)
1808+
1809+
1810+
@pytest.mark.ported_from(
1811+
[
1812+
"https://github.com/ethereum/tests/blob/v13.3/src/GeneralStateTestsFiller/stExtCodeHash/codeCopyZero_ParisFiller.yml", # noqa: E501
1813+
],
1814+
)
1815+
def test_codecopy_zero_in_create2(
1816+
state_test: StateTestFiller,
1817+
pre: Alloc,
1818+
) -> None:
1819+
"""
1820+
Test CODECOPY inside CREATE2 initcode that deploys empty code.
1821+
1822+
The initcode does CODECOPY(0,0,32) which copies the first 32 bytes
1823+
of the initcode itself (not the deployed code). It then checks
1824+
EXTCODESIZE(ADDRESS) and EXTCODEHASH(ADDRESS) of self during init,
1825+
which see the account as having empty code. The deployed contract
1826+
retains the storage set during init.
1827+
"""
1828+
storage = Storage()
1829+
1830+
# Build the initcode that queries itself and deploys empty code.
1831+
# During init: CODECOPY copies the initcode, EXTCODESIZE(self)=0,
1832+
# EXTCODEHASH(self)=keccak256("").
1833+
initcode = (
1834+
Op.CODECOPY(0, 0, 32)
1835+
+ Op.SSTORE(0x50, Op.MLOAD(0))
1836+
+ Op.SSTORE(0x51, Op.EXTCODESIZE(Op.ADDRESS))
1837+
+ Op.SSTORE(0x52, Op.EXTCODEHASH(Op.ADDRESS))
1838+
+ Op.SSTORE(
1839+
0x53,
1840+
Op.EXTCODESIZE(Op.CALLCODE(50_000, Op.ADDRESS, 0, 0, 0, 0, 0)),
1841+
)
1842+
+ Op.EXTCODECOPY(Op.ADDRESS, 0, 0, 32)
1843+
+ Op.SSTORE(0x54, Op.MLOAD(0))
1844+
# Return empty code (size 0).
1845+
+ Op.RETURN(0, 0)
1846+
)
1847+
1848+
# Factory: CREATE2 and return the created address.
1849+
factory_code = (
1850+
Om.MSTORE(initcode, 0)
1851+
+ Op.MSTORE(
1852+
0,
1853+
Op.CREATE2(value=0, offset=0, size=len(initcode), salt=0),
1854+
)
1855+
+ Op.RETURN(0, 32)
1856+
)
1857+
1858+
factory = pre.deploy_contract(factory_code, balance=10**18)
1859+
1860+
# Caller: invoke factory and store created address.
1861+
caller_code = Op.CALL(550_000, factory, 0, 0, 0, 0, 32) + Op.SSTORE(
1862+
storage.store_next(0, "created_address"), Op.MLOAD(0)
1863+
)
1864+
1865+
caller = pre.deploy_contract(caller_code, storage=storage.canary())
1866+
1867+
created = compute_create2_address(
1868+
address=factory, salt=0, initcode=initcode
1869+
)
1870+
storage[0] = created
1871+
1872+
# First 32 bytes of initcode — what CODECOPY(0,0,32) returns.
1873+
initcode_word0 = bytes(initcode)[:32]
1874+
1875+
tx = Transaction(
1876+
sender=pre.fund_eoa(),
1877+
to=caller,
1878+
gas_limit=1_400_000,
1879+
)
1880+
1881+
state_test(
1882+
pre=pre,
1883+
post={
1884+
caller: Account(storage=storage),
1885+
created: Account(
1886+
nonce=1,
1887+
code=b"",
1888+
storage={
1889+
0x50: initcode_word0,
1890+
0x51: 0,
1891+
0x52: keccak256(b""),
1892+
0x53: 0,
1893+
0x54: 0,
1894+
},
1895+
),
1896+
},
1897+
tx=tx,
1898+
)

tests/static/state_tests/stExtCodeHash/__init__.py

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/static/state_tests/stExtCodeHash/codeCopyZero_ParisFiller.yml

Lines changed: 0 additions & 123 deletions
This file was deleted.

0 commit comments

Comments
 (0)