Skip to content

Commit c7d034e

Browse files
committed
EXCHANGE
Exercise exchange operation Signed-off-by: Danno Ferrin <danno@numisight.com>
2 parents 4c668fd + bfbf248 commit c7d034e

7 files changed

Lines changed: 233 additions & 0 deletions

File tree

src/ethereum_test_tools/vm/opcode.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5014,6 +5014,36 @@ class Opcodes(Opcode, Enum):
50145014
50155015
"""
50165016

5017+
EXCHANGE = Opcode(0xE8, data_portion_length=1)
5018+
"""
5019+
!!! Note: This opcode is under development
5020+
5021+
EXCHANGE[mn]
5022+
----
5023+
5024+
Description
5025+
----
5026+
Exchanges two stack positions. Two nybbles, n is high 4 bits + 1, then m is 4 low bits + 1.
5027+
Exchanges tne n+1'th item with the n + m + 1 item.
5028+
5029+
Inputs
5030+
----
5031+
n + m + 1, or ((imm >> 4) + (imm &0x0F) + 3) from the raw immediate,
5032+
5033+
Outputs
5034+
----
5035+
n + m + 1, or ((imm >> 4) + (imm &0x0F) + 3) from the raw immediate,
5036+
5037+
Fork
5038+
----
5039+
EOF_FORK
5040+
5041+
Gas
5042+
----
5043+
3
5044+
5045+
"""
5046+
50175047
EOFCREATE = Opcode(0xEC, popped_stack_items=4, pushed_stack_items=1, data_portion_length=1)
50185048
"""
50195049
!!! Note: This opcode is under development
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""
2+
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
3+
Tests for [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663).
4+
""" # noqa: E501
5+
6+
REFERENCE_SPEC_GIT_PATH = "EIPS/eip-663.md"
7+
REFERENCE_SPEC_VERSION = "6aed382f86258f33603f5dc275956f739aaae096"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Pytest fixtures for EIP-663 tests
3+
"""
4+
import pytest
5+
6+
from ethereum_test_tools import Transaction
7+
8+
9+
@pytest.fixture
10+
def tx() -> Transaction:
11+
"""
12+
Produces the default Transaction.
13+
"""
14+
return Transaction(to=0xC0DE, gas_limit=10_000_000)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""
2+
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
3+
Tests for the DUPN instruction.
4+
""" # noqa: E501
5+
6+
import pytest
7+
8+
from ethereum_test_tools import Account, Environment, StateTestFiller, TestAddress, Transaction
9+
from ethereum_test_tools.eof.v1 import Container, Section
10+
from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION
11+
from ethereum_test_tools.vm.opcode import Opcodes as Op
12+
13+
from ..eip3540_eof_v1.spec import EOF_FORK_NAME
14+
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION
15+
16+
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
17+
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
18+
19+
20+
@pytest.mark.valid_from(EOF_FORK_NAME)
21+
def test_dupn_all_valid_immediates(
22+
tx: Transaction,
23+
state_test: StateTestFiller,
24+
):
25+
"""
26+
Test case for all valid DUPN immediates.
27+
"""
28+
n = 256
29+
values = range(0xD00, 0xD00 + n)
30+
31+
eof_code = Container(
32+
sections=[
33+
Section.Code(
34+
code=b"".join(Op.PUSH2(v) for v in values)
35+
+ b"".join(Op.SSTORE(x, Op.DUPN[x]) for x in range(0, n))
36+
+ Op.STOP,
37+
code_inputs=0,
38+
code_outputs=NON_RETURNING_SECTION,
39+
max_stack_height=n + 2,
40+
)
41+
],
42+
)
43+
44+
pre = {
45+
TestAddress: Account(balance=1_000_000_000),
46+
tx.to: Account(code=eof_code),
47+
}
48+
49+
post = {tx.to: Account(storage=dict(zip(range(0, n), reversed(values))))}
50+
51+
state_test(
52+
env=Environment(),
53+
pre=pre,
54+
post=post,
55+
tx=tx,
56+
)
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
3+
Tests for the SWAPN instruction.
4+
""" # noqa: E501
5+
6+
import pytest
7+
8+
from ethereum_test_tools import Account, Environment, StateTestFiller, TestAddress, Transaction
9+
from ethereum_test_tools.eof.v1 import Container, Section
10+
from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION
11+
from ethereum_test_tools.vm.opcode import Opcodes as Op
12+
13+
from ..eip3540_eof_v1.spec import EOF_FORK_NAME
14+
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION
15+
16+
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
17+
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
18+
19+
20+
@pytest.mark.valid_from(EOF_FORK_NAME)
21+
def test_exchange_all_valid_immediates(
22+
tx: Transaction,
23+
state_test: StateTestFiller,
24+
):
25+
"""
26+
Test case for all valid SWAPN immediates.
27+
"""
28+
n = 256
29+
s = 34
30+
values = range(0x3E8, 0x3E8 + s)
31+
32+
eof_code = Container(
33+
sections=[
34+
Section.Code(
35+
code=b"".join(Op.PUSH2(v) for v in values)
36+
+ b"".join(Op.EXCHANGE(x) for x in range(0, n))
37+
+ b"".join((Op.PUSH1(x) + Op.SSTORE) for x in range(0, s))
38+
+ Op.STOP,
39+
code_inputs=0,
40+
code_outputs=NON_RETURNING_SECTION,
41+
max_stack_height=s + 1,
42+
)
43+
],
44+
)
45+
46+
pre = {
47+
TestAddress: Account(balance=1_000_000_000),
48+
tx.to: Account(code=eof_code),
49+
}
50+
51+
# this does the same full-loop exchange
52+
values_rotated = list(range(0x3E8, 0x3E8 + s))
53+
for e in range(0, n):
54+
a = (e >> 4) + 1
55+
b = (e & 0x0F) + 1 + a
56+
tmp = values_rotated[a]
57+
values_rotated[a] = values_rotated[b]
58+
values_rotated[b] = tmp
59+
60+
post = {tx.to: Account(storage=dict(zip(range(0, s), reversed(values_rotated))))}
61+
62+
state_test(
63+
env=Environment(),
64+
pre=pre,
65+
post=post,
66+
tx=tx,
67+
)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""
2+
abstract: Tests [EIP-663: SWAPN, DUPN and EXCHANGE instructions](https://eips.ethereum.org/EIPS/eip-663)
3+
Tests for the SWAPN instruction.
4+
""" # noqa: E501
5+
6+
import pytest
7+
8+
from ethereum_test_tools import Account, Environment, StateTestFiller, TestAddress, Transaction
9+
from ethereum_test_tools.eof.v1 import Container, Section
10+
from ethereum_test_tools.eof.v1.constants import NON_RETURNING_SECTION
11+
from ethereum_test_tools.vm.opcode import Opcodes as Op
12+
13+
from ..eip3540_eof_v1.spec import EOF_FORK_NAME
14+
from . import REFERENCE_SPEC_GIT_PATH, REFERENCE_SPEC_VERSION
15+
16+
REFERENCE_SPEC_GIT_PATH = REFERENCE_SPEC_GIT_PATH
17+
REFERENCE_SPEC_VERSION = REFERENCE_SPEC_VERSION
18+
19+
20+
@pytest.mark.valid_from(EOF_FORK_NAME)
21+
def test_swapn_all_valid_immediates(
22+
tx: Transaction,
23+
state_test: StateTestFiller,
24+
):
25+
"""
26+
Test case for all valid SWAPN immediates.
27+
"""
28+
n = 256
29+
values = range(0x500, 0x500 + 257)
30+
31+
eof_code = Container(
32+
sections=[
33+
Section.Code(
34+
code=b"".join(Op.PUSH2(v) for v in values)
35+
+ b"".join(Op.SSTORE(x, Op.SWAPN[0xFF - x]) for x in range(0, n))
36+
+ Op.STOP,
37+
code_inputs=0,
38+
code_outputs=NON_RETURNING_SECTION,
39+
max_stack_height=n + 2,
40+
)
41+
],
42+
)
43+
44+
pre = {
45+
TestAddress: Account(balance=1_000_000_000),
46+
tx.to: Account(code=eof_code),
47+
}
48+
49+
values_rotated = list(values[1:]) + [values[0]]
50+
post = {tx.to: Account(storage=dict(zip(range(0, n), reversed(values_rotated))))}
51+
52+
state_test(
53+
env=Environment(),
54+
pre=pre,
55+
post=post,
56+
tx=tx,
57+
)

whitelist.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ dup
9292
dunder
9393
EEST
9494
eip
95+
eip3540
9596
eips
9697
EIPs
9798
endianness
@@ -159,6 +160,7 @@ https
159160
hyperledger
160161
ignoreRevsFile
161162
img
163+
immediates
162164
incrementing
163165
init
164166
initcode

0 commit comments

Comments
 (0)