From c556fa6aa924bcaa44f85303afce852341fda37c Mon Sep 17 00:00:00 2001 From: spencer-tb Date: Tue, 9 Jan 2024 14:02:58 +0000 Subject: [PATCH] tests: refactor callcode gas test. --- docs/CHANGELOG.md | 2 + tests/frontier/opcodes/test_callcode | 0 tests/frontier/opcodes/test_callcode.py | 117 ++++++++++++++++ tests/shanghai/callcode_test/__init__.py | 3 - tests/shanghai/callcode_test/test_callcode.py | 128 ------------------ whitelist.txt | 1 + 6 files changed, 120 insertions(+), 131 deletions(-) create mode 100644 tests/frontier/opcodes/test_callcode create mode 100644 tests/frontier/opcodes/test_callcode.py delete mode 100644 tests/shanghai/callcode_test/__init__.py delete mode 100644 tests/shanghai/callcode_test/test_callcode.py diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b87773ea71..21a87c4707 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -8,6 +8,8 @@ Test fixtures for use by clients are available for each release on the [Github r ### 🧪 Test Cases +- ✨ Adds a callcode gas test when call stipend is applied to gas limit. Covers a bug found in the EthereumJS EVM. ([#371](https://github.com/ethereum/execution-spec-tests/pull/371)) + ### 🛠️ Framework - ✨ Add a `--single-fixture-per-file` flag to generate one fixture JSON file per test case ([#331](https://github.com/ethereum/execution-spec-tests/pull/331)). diff --git a/tests/frontier/opcodes/test_callcode b/tests/frontier/opcodes/test_callcode new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/frontier/opcodes/test_callcode.py b/tests/frontier/opcodes/test_callcode.py new file mode 100644 index 0000000000..bac5d41fe0 --- /dev/null +++ b/tests/frontier/opcodes/test_callcode.py @@ -0,0 +1,117 @@ +""" +abstract: Tests the CALLCODE gas consumption with a CALL/CALLCODE value transfer. + + Tests an EthereumJS bug where the CALLCODE gas was incorrectly calculated from a + CALL value transfer: https://github.com/ethereumjs/ethereumjs-monorepo/issues/3194 + + Test setup: 2 smart contract accounts exist where AAAB CALL/CALLCODEs AAAA, which then + CALLCODEs a non-existent contract AAAC. The result of AAAA's CALL/CALLCODE is stored in AAAB's + storage slot 0. +""" + +from typing import Dict + +import pytest + +from ethereum_test_tools import ( + Account, + Environment, + StateTestFiller, + TestAddress, + Transaction, + to_address, +) +from ethereum_test_tools.vm.opcode import Opcodes as Op + + +@pytest.fixture +def caller_code(caller_gas, caller_type): + """ + Code to call the callee contract: + PUSH1 0x00 + DUP1 * 4 + PUSH2 0xAAAA + PUSH2 call_gas + CALL/CALLCODE + PUSH1 0x00 + SSTORE + """ + return Op.SSTORE(0, caller_type(caller_gas, 0xAAAA, 0, 0, 0, 0, 0)) + + +@pytest.fixture +def callee_code(): + """ + Code called by the caller contract: + PUSH1 0x00 * 4 + PUSH1 0x00 + PUSH1 0x00 + PUSH2 0xAACC + PUSH2 0x1a90 + CALLCODE + """ + return Op.CALLCODE(0x1A90, 0xAACC, 1, 0, 0, 0, 0) + + +@pytest.fixture +def caller_tx() -> Transaction: + """ + Transaction that calls the callee contract. + """ + return Transaction( + chain_id=0x01, + nonce=0, + to=to_address(0xAAAB), + value=1, + gas_limit=500000, + gas_price=7, + ) + + +@pytest.fixture +def pre(callee_code, caller_code) -> Dict[str, Account]: # noqa: D103 + return { + to_address(0xAAAA): Account( + balance=0x03, + code=callee_code, + nonce=1, + ), + to_address(0xAAAB): Account( + balance=0x03, + code=caller_code, + nonce=1, + ), + TestAddress: Account( + balance=0x0BA1A5CE, + ), + } + + +@pytest.fixture +def post(caller_gas) -> Dict[str, Account]: # noqa: D103 + return { + to_address(0xAAAB): Account(storage={0x00: (0x01 if caller_gas >= 0x3D50 else 0x00)}), + } + + +@pytest.mark.parametrize( + "caller_gas", + [0x2D5F, 0x3D50], + ids=["insufficient", "sufficient"], +) +@pytest.mark.parametrize( + "caller_type", + [Op.CALL, Op.CALLCODE], +) +@pytest.mark.valid_from("London") +@pytest.mark.valid_until("Shanghai") +def test_callcode_gas_with_call( + state_test: StateTestFiller, + pre: Dict[str, Account], + caller_tx: Transaction, + post: Dict[str, Account], +): + """ + Tests the CALLCODE gas consumption with a CALL/CALLCODE value transfer. + """ + state_test(env=Environment(), pre=pre, post=post, txs=[caller_tx]) diff --git a/tests/shanghai/callcode_test/__init__.py b/tests/shanghai/callcode_test/__init__.py deleted file mode 100644 index 46b4f40cc6..0000000000 --- a/tests/shanghai/callcode_test/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -""" -Test cases for CALLCODE edgecase. -""" diff --git a/tests/shanghai/callcode_test/test_callcode.py b/tests/shanghai/callcode_test/test_callcode.py deleted file mode 100644 index f51b7e880e..0000000000 --- a/tests/shanghai/callcode_test/test_callcode.py +++ /dev/null @@ -1,128 +0,0 @@ -""" -abstract: Test CALLCODE gas consumption with value transfer -Tests for an EthereumJS bug where CALLCODE gas was incorrectly calculated when a call had -value transfer -https://github.com/ethereumjs/ethereumjs-monorepo/issues/3194 - -Test setup is 2 contracts where AAAB CALLs AAAA which then CALLCODEs a non-existent contract -AAAC. The CALLCODE result is stored in AAAB's slot 0. - -Bytecode for AAAB contract (used to call contract AAAA and stores result of call execution) -``` -PUSH1 0x00 -DUP1 -DUP1 -DUP1 -DUP1 -PUSH2 0xAAAA -PUSH2 0x12D5 <- This is the gas limit set for the CALL/CODE execution when insufficient for -CALLCODE execution -CALL -PUSH1 0x00 -SSTORE -``` - -Bytecode for AAAA contract (used to check CALL/CALLCODE execution when gas is less -than/sufficient for execution) -``` -PUSH1 0x00 -PUSH1 0x00 -PUSH1 0x00 -PUSH1 0x00 -PUSH1 0x01 -PUSH2 0xAACC -PUSH2 0x1a90 -CALLCODE/CALL -``` -""" - -import pytest - -from ethereum_test_forks import Fork -from ethereum_test_tools import Account, Environment, StateTestFiller, Transaction - - -@pytest.mark.valid_from("London") -@pytest.mark.valid_until("Shanghai") -def test_callcode_with_gas(state_test: StateTestFiller, fork: Fork): - """ - Test CALLCODE gas consumption case with sufficient gas - """ - env = Environment() - - pre = { - "0x000000000000000000000000000000000000aaaa": Account( - balance=0x03, - code="0x6000600060006000600161AACC611A90F2", - nonce=1, - ), - "0x000000000000000000000000000000000000aaab": Account( - balance=0x03, - code="0x60008080808061AAAA613d50f1600055", - nonce=1, - ), - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": Account( - balance=0xFFFFFFFFF, - nonce=0, - ), - } - - tx = Transaction( - ty=1, - chain_id=0x01, - nonce=0, - to="0x000000000000000000000000000000000000aaab", - value=1, - gas_limit=500000, - gas_price=7, - secret_key="0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - protected=True, - ) - - post = { - "0x000000000000000000000000000000000000aaab": Account(storage={0x00: 0x01}), - } - state_test(env=env, pre=pre, post=post, txs=[tx]) - - -@pytest.mark.valid_from("London") -@pytest.mark.valid_until("Shanghai") -def test_callcode_without_gas(state_test: StateTestFiller, fork: Fork): - """ - Test CALLCODE gas consumption without sufficient gas - """ - env = Environment() - - pre = { - "0x000000000000000000000000000000000000aaaa": Account( - balance=0x03, - code="0x6000600060006000600161AACC611A90F2", - nonce=1, - ), - "0x000000000000000000000000000000000000aaab": Account( - balance=0x03, - code="0x60008080808061AAAA612d5ff1600055", - nonce=1, - ), - "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": Account( - balance=0xFFFFFFFFF, - nonce=0, - ), - } - - tx = Transaction( - ty=0, - chain_id=0x01, - nonce=0, - to="0x000000000000000000000000000000000000aaab", - value=1, - gas_limit=2500000, - gas_price=7, - secret_key="0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", - protected=True, - ) - - post = { - "0x000000000000000000000000000000000000aaab": Account(storage={0x00: 0x00}), - } - state_test(env=env, pre=pre, post=post, txs=[tx]) diff --git a/whitelist.txt b/whitelist.txt index a735aef42e..de5b9d78ba 100644 --- a/whitelist.txt +++ b/whitelist.txt @@ -81,6 +81,7 @@ esbenp eth ethash ethereum +EthereumJS ethereum's evaluatable evm