Skip to content

Commit b28a86c

Browse files
authored
new(tests): EIP-663: Add simple swap/dup/exchange tests (#1373)
* Add simple swap/dup/exchange tests Add simple and asymmetric tests for swap/dup/exchange, to detect when operations are performed on the wrong end of the stack. Signed-off-by: Danno Ferrin <[email protected]> * update target fork Signed-off-by: Danno Ferrin <[email protected]> --------- Signed-off-by: Danno Ferrin <[email protected]>
1 parent e1e5b34 commit b28a86c

File tree

3 files changed

+147
-11
lines changed

3 files changed

+147
-11
lines changed

tests/osaka/eip7692_eof_v1/eip663_dupn_swapn_exchange/test_dupn.py

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55

66
import pytest
77

8-
from ethereum_test_tools import Account, EOFException, EOFStateTestFiller, EOFTestFiller
8+
from ethereum_test_tools import (
9+
Account,
10+
Alloc,
11+
Environment,
12+
EOFException,
13+
EOFStateTestFiller,
14+
EOFTestFiller,
15+
StateTestFiller,
16+
Transaction,
17+
)
918
from ethereum_test_tools.eof.v1 import Container, Section
1019
from ethereum_test_tools.eof.v1.constants import MAX_OPERAND_STACK_HEIGHT
1120
from ethereum_test_tools.vm.opcode import Opcodes as Op
@@ -16,8 +25,9 @@
1625
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
1726
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
1827

28+
pytestmark = pytest.mark.valid_from(EOF_FORK_NAME)
29+
1930

20-
@pytest.mark.valid_from(EOF_FORK_NAME)
2131
def test_dupn_all_valid_immediates(eof_state_test: EOFStateTestFiller):
2232
"""Test case for all valid DUPN immediates."""
2333
n = 2**8
@@ -53,7 +63,6 @@ def test_dupn_all_valid_immediates(eof_state_test: EOFStateTestFiller):
5363
[2**8 - 1, 2**8],
5464
],
5565
)
56-
@pytest.mark.valid_from(EOF_FORK_NAME)
5766
def test_dupn_stack_underflow(
5867
stack_height: int,
5968
max_stack_height: int,
@@ -85,7 +94,6 @@ def test_dupn_stack_underflow(
8594
[2**8 - 1, MAX_OPERAND_STACK_HEIGHT + 1, EOFException.MAX_STACK_HEIGHT_ABOVE_LIMIT],
8695
],
8796
)
88-
@pytest.mark.valid_from(EOF_FORK_NAME)
8997
def test_dupn_stack_overflow(
9098
dupn_operand: int,
9199
max_stack_height: int,
@@ -107,3 +115,38 @@ def test_dupn_stack_overflow(
107115
container=eof_code,
108116
expect_exception=expect_exception,
109117
)
118+
119+
120+
@pytest.mark.parametrize(
121+
"dupn_arg,stack_height", [pytest.param(5, 9, id="5_of_9"), pytest.param(12, 30, id="12_of_30")]
122+
)
123+
def test_dupn_simple(
124+
stack_height: int,
125+
dupn_arg: int,
126+
pre: Alloc,
127+
state_test: StateTestFiller,
128+
):
129+
"""Test case for simple DUPN operations."""
130+
sender = pre.fund_eoa()
131+
contract_address = pre.deploy_contract(
132+
code=Container(
133+
sections=[
134+
Section.Code(
135+
code=sum(Op.PUSH2[v] for v in range(stack_height, 0, -1))
136+
+ Op.DUPN[dupn_arg]
137+
+ sum((Op.PUSH1(v) + Op.SSTORE) for v in range(0, stack_height + 1))
138+
+ Op.STOP,
139+
max_stack_height=stack_height + 2,
140+
)
141+
],
142+
)
143+
)
144+
145+
storage = {v: v for v in range(1, stack_height + 1)}
146+
storage[0] = dupn_arg + 1
147+
print(storage)
148+
post = {contract_address: Account(storage=storage)}
149+
150+
tx = Transaction(to=contract_address, sender=sender, gas_limit=10_000_000)
151+
152+
state_test(env=Environment(), pre=pre, post=post, tx=tx)

tests/osaka/eip7692_eof_v1/eip663_dupn_swapn_exchange/test_exchange.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55

66
import pytest
77

8-
from ethereum_test_tools import Account, EOFException, EOFStateTestFiller, EOFTestFiller
8+
from ethereum_test_tools import (
9+
Account,
10+
Alloc,
11+
Environment,
12+
EOFException,
13+
EOFStateTestFiller,
14+
EOFTestFiller,
15+
StateTestFiller,
16+
Transaction,
17+
)
918
from ethereum_test_tools.eof.v1 import Container, Section
1019
from ethereum_test_tools.vm.opcode import Opcodes as Op
1120

@@ -15,8 +24,9 @@
1524
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
1625
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
1726

27+
pytestmark = pytest.mark.valid_from(EOF_FORK_NAME)
28+
1829

19-
@pytest.mark.valid_from(EOF_FORK_NAME)
2030
def test_exchange_all_valid_immediates(eof_state_test: EOFStateTestFiller):
2131
"""Test case for all valid EXCHANGE immediates."""
2232
n = 256
@@ -65,7 +75,6 @@ def test_exchange_all_valid_immediates(eof_state_test: EOFStateTestFiller):
6575
pytest.param(32, 17, 33, id="stack_height=32_n=16_m=16"),
6676
],
6777
)
68-
@pytest.mark.valid_from(EOF_FORK_NAME)
6978
def test_exchange_all_invalid_immediates(
7079
eof_test: EOFTestFiller,
7180
stack_height: int,
@@ -89,3 +98,43 @@ def test_exchange_all_invalid_immediates(
8998
container=eof_code,
9099
expect_exception=EOFException.STACK_UNDERFLOW,
91100
)
101+
102+
103+
@pytest.mark.parametrize(
104+
"m_arg,n_arg,extra_stack",
105+
[pytest.param(0, 0, 3, id="m0_n0_extra3"), pytest.param(2, 3, 7, id="m2_n3_extra7")],
106+
)
107+
def test_exchange_simple(
108+
m_arg: int,
109+
n_arg: int,
110+
extra_stack: int,
111+
pre: Alloc,
112+
state_test: StateTestFiller,
113+
):
114+
"""Test case for simple EXCHANGE operations."""
115+
sender = pre.fund_eoa()
116+
stack_height = m_arg + n_arg + 2 + extra_stack
117+
contract_address = pre.deploy_contract(
118+
code=Container(
119+
sections=[
120+
Section.Code(
121+
code=sum(Op.PUSH2[v] for v in range(stack_height, 0, -1))
122+
+ Op.EXCHANGE[m_arg << 4 | n_arg]
123+
+ sum((Op.PUSH1(v) + Op.SSTORE) for v in range(1, stack_height + 1))
124+
+ Op.STOP,
125+
max_stack_height=stack_height + 1,
126+
)
127+
],
128+
)
129+
)
130+
131+
storage = {v: v for v in range(1, stack_height + 1)}
132+
first = m_arg + 2 # one based index, plus m=0 means first non-top item
133+
second = first + n_arg + 1 # n+1 past m
134+
storage[first], storage[second] = storage[second], storage[first]
135+
print(storage)
136+
post = {contract_address: Account(storage=storage)}
137+
138+
tx = Transaction(to=contract_address, sender=sender, gas_limit=10_000_000)
139+
140+
state_test(env=Environment(), pre=pre, post=post, tx=tx)

tests/osaka/eip7692_eof_v1/eip663_dupn_swapn_exchange/test_swapn.py

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55

66
import pytest
77

8-
from ethereum_test_tools import Account, EOFException, EOFStateTestFiller, EOFTestFiller
8+
from ethereum_test_tools import (
9+
Account,
10+
Alloc,
11+
Environment,
12+
EOFException,
13+
EOFStateTestFiller,
14+
EOFTestFiller,
15+
StateTestFiller,
16+
Transaction,
17+
)
918
from ethereum_test_tools.eof.v1 import Container, Section
1019
from ethereum_test_tools.eof.v1.constants import MAX_OPERAND_STACK_HEIGHT
1120
from ethereum_test_tools.vm.opcode import Opcodes as Op
@@ -16,8 +25,9 @@
1625
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
1726
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
1827

28+
pytestmark = pytest.mark.valid_from(EOF_FORK_NAME)
29+
1930

20-
@pytest.mark.valid_from(EOF_FORK_NAME)
2131
def test_swapn_all_valid_immediates(eof_state_test: EOFStateTestFiller):
2232
"""Test case for all valid SWAPN immediates."""
2333
n = 256
@@ -50,7 +60,6 @@ def test_swapn_all_valid_immediates(eof_state_test: EOFStateTestFiller):
5060
2**8 - 1,
5161
],
5262
)
53-
@pytest.mark.valid_from(EOF_FORK_NAME)
5463
def test_swapn_on_max_stack(
5564
swapn_operand: int,
5665
eof_test: EOFTestFiller,
@@ -79,7 +88,6 @@ def test_swapn_on_max_stack(
7988
2**8 - 1,
8089
],
8190
)
82-
@pytest.mark.valid_from(EOF_FORK_NAME)
8391
def test_swapn_stack_underflow(
8492
stack_height: int,
8593
eof_test: EOFTestFiller,
@@ -99,3 +107,39 @@ def test_swapn_stack_underflow(
99107
container=eof_code,
100108
expect_exception=EOFException.STACK_UNDERFLOW,
101109
)
110+
111+
112+
@pytest.mark.parametrize(
113+
"swapn_arg,stack_height",
114+
[pytest.param(5, 9, id="5_of_9"), pytest.param(12, 30, id="12_of_30")],
115+
)
116+
def test_swapn_simple(
117+
stack_height: int,
118+
swapn_arg: int,
119+
pre: Alloc,
120+
state_test: StateTestFiller,
121+
):
122+
"""Test case for simple SWAPN operations."""
123+
sender = pre.fund_eoa()
124+
contract_address = pre.deploy_contract(
125+
code=Container(
126+
sections=[
127+
Section.Code(
128+
code=sum(Op.PUSH2[v] for v in range(stack_height, 0, -1))
129+
+ Op.SWAPN[swapn_arg]
130+
+ sum((Op.PUSH1(v) + Op.SSTORE) for v in range(1, stack_height + 1))
131+
+ Op.STOP,
132+
max_stack_height=stack_height + 1,
133+
)
134+
],
135+
)
136+
)
137+
138+
storage = {v: v for v in range(1, stack_height + 1)}
139+
storage[1], storage[swapn_arg + 2] = storage[swapn_arg + 2], storage[1]
140+
print(storage)
141+
post = {contract_address: Account(storage=storage)}
142+
143+
tx = Transaction(to=contract_address, sender=sender, gas_limit=10_000_000)
144+
145+
state_test(env=Environment(), pre=pre, post=post, tx=tx)

0 commit comments

Comments
 (0)