Skip to content

Commit

Permalink
fix(tests): Fix Existing EOF + EIP-7702 Tests (ethereum#821)
Browse files Browse the repository at this point in the history
* fix(tools): Conditional EOF generator

* fix(tests): EOF + EIP-7702 tests

* docs: changelog

* Update docs/CHANGELOG.md
  • Loading branch information
marioevz authored Sep 19, 2024
1 parent 12c0087 commit c0a381c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 18 deletions.
2 changes: 2 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Example test `tests/frontier/opcodes/test_dup.py` now includes EOF parametrization ([#610](https://github.com/ethereum/execution-spec-tests/pull/610)).
- ✨ Convert all opcodes validation test `tests/frontier/opcodes/test_all_opcodes.py` ([#748](https://github.com/ethereum/execution-spec-tests/pull/748)).
- ✨ Update [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) tests for Devnet-3 ([#733](https://github.com/ethereum/execution-spec-tests/pull/733))
- 🐞 Fix [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702)+EOF tests due to incorrect test expectations and faulty `Conditional` test generator in EOF mode ([#821](https://github.com/ethereum/execution-spec-tests/pull/821))

### 🛠️ Framework

Expand All @@ -38,6 +39,7 @@ Test fixtures for use by clients are available for each release on the [Github r
- ✨ Added `selector` and `marks` fields to all `@pytest.mark.with_all*` markers, which allows passing lambda functions to select or mark specific parametrized values (see [documentation](https://ethereum.github.io/execution-spec-tests/main/writing_tests/test_markers/#covariant-marker-keyword-arguments) for more information) ([#762](https://github.com/ethereum/execution-spec-tests/pull/762)).
- ✨ Improves consume input flags for develop and stable fixture releases, fixes `--help` flag for consume ([#745](https://github.com/ethereum/execution-spec-tests/pull/745)).
- 🐞 Fix erroneous fork message in pytest session header with development forks ([#806](https://github.com/ethereum/execution-spec-tests/pull/806)).
- 🐞 Fix `Conditional` code generator in EOF mode ([#821](https://github.com/ethereum/execution-spec-tests/pull/821))

### 🔧 EVM Tools

Expand Down
12 changes: 8 additions & 4 deletions src/ethereum_test_tools/code/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,17 @@ def __new__(
# Then we need to do the conditional jump by skipping the false branch
condition = Op.JUMPI(Op.ADD(Op.PC, len(if_false) + 3), condition)

# Finally we append the condition, false and true branches, plus the jumpdest at the
# very end
bytecode = condition + if_false + if_true + Op.JUMPDEST

elif evm_code_type == EVMCodeType.EOF_V1:
if_false += Op.RJUMP[len(if_true)]
if not if_false.terminating:
if_false += Op.RJUMP[len(if_true)]
condition = Op.RJUMPI[len(if_false)](condition)

# Finally we append the true and false branches, and the condition, plus the jumpdest at
# the very end
bytecode = condition + if_false + if_true + Op.JUMPDEST
# Finally we append the condition, false and true branches
bytecode = condition + if_false + if_true

return super().__new__(cls, bytecode)

Expand Down
109 changes: 95 additions & 14 deletions tests/prague/eip7702_set_code_tx/test_set_code_txs.py
Original file line number Diff line number Diff line change
Expand Up @@ -792,7 +792,7 @@ def test_tx_into_self_delegating_set_code(
pre: Alloc,
):
"""
Test a transaction that has entry-point into a set-code address that delegates to itself.
Test a transaction that has entry-point into a set-code account that delegates to itself.
"""
auth_signer = pre.fund_eoa(auth_account_start_balance)

Expand Down Expand Up @@ -828,7 +828,8 @@ def test_tx_into_chain_delegating_set_code(
pre: Alloc,
):
"""
Test a transaction that has entry-point into a set-code address that delegates to itself.
Test a transaction that has entry-point into a set-code account that delegates to another
set-code account.
"""
auth_signer_1 = pre.fund_eoa(auth_account_start_balance)
auth_signer_2 = pre.fund_eoa(auth_account_start_balance)
Expand Down Expand Up @@ -870,14 +871,20 @@ def test_call_into_self_delegating_set_code(
call_opcode: Op,
):
"""
Test a transaction that has entry-point into a set-code address that delegates to itself.
Test call into a set-code account that delegates to itself.
"""
auth_signer = pre.fund_eoa(auth_account_start_balance)

storage = Storage()
entry_code = (
Op.SSTORE(
storage.store_next(call_return_code(opcode=call_opcode, success=False)),
storage.store_next(
call_return_code(
opcode=call_opcode,
success=False,
revert=(call_opcode == Op.EXTDELEGATECALL),
)
),
call_opcode(address=auth_signer),
)
+ Op.STOP
Expand Down Expand Up @@ -916,15 +923,21 @@ def test_call_into_chain_delegating_set_code(
call_opcode: Op,
):
"""
Test a transaction that has entry-point into a set-code address that delegates to itself.
Test call into a set-code account that delegates to another set-code account.
"""
auth_signer_1 = pre.fund_eoa(auth_account_start_balance)
auth_signer_2 = pre.fund_eoa(auth_account_start_balance)

storage = Storage()
entry_code = (
Op.SSTORE(
storage.store_next(call_return_code(opcode=call_opcode, success=False)),
storage.store_next(
call_return_code(
opcode=call_opcode,
success=False,
revert=(call_opcode == Op.EXTDELEGATECALL),
)
),
call_opcode(address=auth_signer_1),
)
+ Op.STOP
Expand Down Expand Up @@ -1117,12 +1130,7 @@ def test_ext_code_on_self_set_code(
)


@pytest.mark.with_all_call_opcodes(
selector=(
lambda opcode: opcode
not in [Op.STATICCALL, Op.CALLCODE, Op.DELEGATECALL, Op.EXTDELEGATECALL, Op.EXTSTATICCALL]
)
)
@pytest.mark.with_all_evm_code_types()
@pytest.mark.parametrize(
"set_code_address_first",
[
Expand All @@ -1133,7 +1141,6 @@ def test_ext_code_on_self_set_code(
def test_set_code_address_and_authority_warm_state(
state_test: StateTestFiller,
pre: Alloc,
call_opcode: Op,
set_code_address_first: bool,
):
"""
Expand All @@ -1150,6 +1157,7 @@ def test_set_code_address_and_authority_warm_state(
set_code = Op.STOP
set_code_to_address = pre.deploy_contract(set_code)

call_opcode = Op.CALL
overhead_cost = 3 * len(call_opcode.kwargs) # type: ignore
if call_opcode == Op.CALL:
overhead_cost -= 1 # GAS opcode is less expensive than a PUSH
Expand All @@ -1176,7 +1184,7 @@ def test_set_code_address_and_authority_warm_state(
callee_code += code_gas_measure_authority + code_gas_measure_set_code
callee_code += Op.SSTORE(slot_call_success, 1) + Op.STOP

callee_address = pre.deploy_contract(callee_code)
callee_address = pre.deploy_contract(callee_code, evm_code_type=EVMCodeType.LEGACY)
callee_storage = Storage()
callee_storage[slot_call_success] = 1
callee_storage[slot_set_code_to_warm_state] = 2_600 if set_code_address_first else 100
Expand Down Expand Up @@ -1210,6 +1218,79 @@ def test_set_code_address_and_authority_warm_state(
)


@pytest.mark.with_all_call_opcodes()
@pytest.mark.parametrize(
"set_code_address_first",
[
pytest.param(True, id="call_set_code_address_first_then_authority"),
pytest.param(False, id="call_authority_first_then_set_code_address"),
],
)
def test_set_code_address_and_authority_warm_state_call_types(
state_test: StateTestFiller,
pre: Alloc,
call_opcode: Op,
set_code_address_first: bool,
):
"""
Test set to code address and authority warm status after a call to
authority address, or viceversa, using all available call opcodes
without using `GAS` opcode (unavailable in EOF).
"""
auth_signer = pre.fund_eoa(auth_account_start_balance)

slot = count(1)
slot_call_return_code = next(slot)
slot_call_success = next(slot)

set_code = Op.STOP
set_code_to_address = pre.deploy_contract(set_code)

call_set_code_to_address = Op.SSTORE(
slot_call_return_code, call_opcode(address=set_code_to_address)
)
call_authority_address = Op.SSTORE(slot_call_return_code, call_opcode(address=auth_signer))

callee_code = Bytecode()
if set_code_address_first:
callee_code += call_set_code_to_address + call_authority_address
else:
callee_code += call_authority_address + call_set_code_to_address
callee_code += Op.SSTORE(slot_call_success, 1) + Op.STOP

callee_address = pre.deploy_contract(callee_code)
callee_storage = Storage()
callee_storage[slot_call_return_code] = call_return_code(opcode=call_opcode, success=True)
callee_storage[slot_call_success] = 1

tx = Transaction(
gas_limit=1_000_000,
to=callee_address,
authorization_list=[
AuthorizationTuple(
address=set_code_to_address,
nonce=0,
signer=auth_signer,
),
],
sender=pre.fund_eoa(),
)

state_test(
env=Environment(),
pre=pre,
tx=tx,
post={
callee_address: Account(storage=callee_storage),
auth_signer: Account(
nonce=1,
code=Spec.delegation_designation(set_code_to_address),
balance=auth_account_start_balance,
),
},
)


@pytest.mark.parametrize(
"balance",
[0, 1],
Expand Down

0 comments on commit c0a381c

Please sign in to comment.