Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: specify the target evm version to solc for yul compilation #147

Merged
Merged
2 changes: 1 addition & 1 deletion .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
matrix:
python: ['3.10']
golang: [1.19]
solc: ['0.8.17']
solc: ['0.8.20']
steps:
- uses: actions/checkout@v2
with:
Expand Down
3 changes: 2 additions & 1 deletion src/ethereum_test_tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
tests.
"""

from .code import Code, CodeGasMeasure, Initcode, Yul
from .code import Code, CodeGasMeasure, Initcode, Yul, YulCompiler
from .common import (
AccessList,
Account,
Expand Down Expand Up @@ -59,6 +59,7 @@
"Transaction",
"Withdrawal",
"Yul",
"YulCompiler",
"add_kzg_version",
"ceiling_division",
"compute_create_address",
Expand Down
3 changes: 2 additions & 1 deletion src/ethereum_test_tools/code/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
"""
from .code import Code, code_to_bytes, code_to_hex
from .generators import CodeGasMeasure, Initcode
from .yul import Yul
from .yul import Yul, YulCompiler

__all__ = (
"Code",
"CodeGasMeasure",
"Initcode",
"Yul",
"YulCompiler",
"code_to_bytes",
"code_to_hex",
)
86 changes: 77 additions & 9 deletions src/ethereum_test_tools/code/yul.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,37 @@
Yul frontend
"""

import re
from pathlib import Path
from shutil import which
from subprocess import PIPE, run
from typing import Optional
from typing import Mapping, Optional, Tuple, Type, Union

from ethereum_test_forks import Fork

from .code import Code

SOLC: Path = Path("solc")
SOLC_ARGS = (
SOLC,
"--assemble",
"-",
)
DEFAULT_SOLC_ARGS = ("--assemble", "-")


def get_evm_version_from_fork(fork: Fork | None):
"""
Get the solc evm version corresponding to `fork`.

Args
----
fork (Fork): The fork to retrieve the corresponding evm version for.

Returns
-------
str: The name of evm version as required by solc's --evm-version.
"""
if not fork:
return None
fork_to_evm_version_map: Mapping[str, str] = {"Merge": "paris"}
marioevz marked this conversation as resolved.
Show resolved Hide resolved
if fork.name() in fork_to_evm_version_map:
return fork_to_evm_version_map[fork.name()]
return fork.name().lower()


class Yul(Code):
Expand All @@ -25,16 +44,43 @@ class Yul(Code):
source: str
compiled: Optional[bytes] = None

def __init__(self, source: str):
def __init__(
self,
source: str,
fork: Optional[Fork] = None,
binary: Optional[Path | str] = None,
):
self.source = source
self.evm_version = get_evm_version_from_fork(fork)
if binary is None:
which_path = which("solc")
if which_path is not None:
binary = Path(which_path)
if binary is None or not Path(binary).exists():
raise Exception(
"""`solc` binary executable not found, please refer to
https://docs.soliditylang.org/en/latest/installing-solidity.html
for help downloading and installing `solc`"""
)
self.binary = Path(binary)

def assemble(self) -> bytes:
"""
Assembles using `solc --assemble`.
"""
if not self.compiled:
solc_args: Tuple[Union[Path, str], ...] = ()
if self.evm_version:
solc_args = (
self.binary,
"--evm-version",
self.evm_version,
*DEFAULT_SOLC_ARGS,
)
else:
solc_args = (self.binary, *DEFAULT_SOLC_ARGS)
result = run(
SOLC_ARGS,
solc_args,
input=str.encode(self.source),
stdout=PIPE,
stderr=PIPE,
Expand All @@ -51,3 +97,25 @@ def assemble(self) -> bytes:

self.compiled = bytes.fromhex(hex_str)
return self.compiled

def version(self) -> str:
"""
Return solc's version string
"""
result = run(
[self.binary, "--version"],
stdout=PIPE,
stderr=PIPE,
)
solc_output = result.stdout.decode().split("\n")
version_pattern = r"0\.\d+\.\d+\+\S+"
solc_version_string = None
for line in solc_output:
match = re.search(version_pattern, line)
if match:
solc_version_string = match.group(0)
break
return solc_version_string


YulCompiler = Type[Yul]
5 changes: 4 additions & 1 deletion src/ethereum_test_tools/tests/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import pytest

from ethereum_test_forks import Merge

from ..code import Code, Initcode, Yul, code_to_bytes


Expand Down Expand Up @@ -96,7 +98,8 @@ def test_yul():
expected_bytecode += bytes.fromhex("80")
expected_bytecode += bytes.fromhex("55")

assert Yul(long_code).assemble() == expected_bytecode
# TODO(dan): workaround until it's understood why Shanghai takes so long to compile
assert Yul(long_code, fork=Merge).assemble() == expected_bytecode
marioevz marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.parametrize(
Expand Down
41 changes: 26 additions & 15 deletions src/ethereum_test_tools/tests/test_filler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ def test_make_genesis(fork: Fork, hash: str):
sstore(0, f(1, 2))
return(0, 32)
}
"""
""",
fork=fork,
),
),
TestAddress: Account(balance=0x0BA1A9CE0BA1A9CE),
Expand Down Expand Up @@ -127,7 +128,8 @@ def test_fill_state_test(fork: Fork, expected_json_file: str):
assert fixture_json == expected


def test_fill_london_blockchain_test_valid_txs():
@pytest.mark.parametrize("fork", [London])
def test_fill_london_blockchain_test_valid_txs(fork: Fork):
"""
Test `ethereum_test.filler.fill_fixtures` with `BlockchainTest`.
"""
Expand All @@ -147,7 +149,8 @@ def test_fill_london_blockchain_test_valid_txs():
sstore(add(number(), 0x2000), selfbalance())
stop()
}
"""
""",
fork=fork,
),
),
"0xcccccccccccccccccccccccccccccccccccccccd": Account(
Expand All @@ -160,7 +163,8 @@ def test_fill_london_blockchain_test_valid_txs():
0xcccccccccccccccccccccccccccccccccccccccc,
0, 0, 0, 0)
}
"""
""",
fork=fork,
),
),
"0x000000000000000000000000000000000000c0de": Account(
Expand All @@ -173,7 +177,8 @@ def test_fill_london_blockchain_test_valid_txs():
0xcccccccccccccccccccccccccccccccccccccccc,
0, 0, 0, 0)
}
"""
""",
fork=fork,
),
),
"0xccccccccccccccccccccccccccccccccccccccce": Account(
Expand All @@ -188,7 +193,8 @@ def test_fill_london_blockchain_test_valid_txs():
0xcccccccccccccccccccccccccccccccccccccccc,
0, 0, 0, 0)
}
"""
""",
fork=fork,
),
),
}
Expand Down Expand Up @@ -387,11 +393,11 @@ def test_fill_london_blockchain_test_valid_txs():
t8n = EvmTransitionTool()

fixture = {
f"000/my_blockchain_test/{London}": fill_test(
f"000/my_blockchain_test/{fork.name()}": fill_test(
t8n=t8n,
b11r=b11r,
test_spec=blockchain_test,
fork=London,
fork=fork,
engine="NoProof",
spec=None,
)
Expand All @@ -413,7 +419,8 @@ def test_fill_london_blockchain_test_valid_txs():
assert fixture_json == expected


def test_fill_london_blockchain_test_invalid_txs():
@pytest.mark.parametrize("fork", [London])
def test_fill_london_blockchain_test_invalid_txs(fork):
"""
Test `ethereum_test.filler.fill_fixtures` with `BlockchainTest`.
"""
Expand All @@ -433,7 +440,8 @@ def test_fill_london_blockchain_test_invalid_txs():
sstore(add(number(), 0x2000), selfbalance())
stop()
}
"""
""",
fork=fork,
),
),
"0xcccccccccccccccccccccccccccccccccccccccd": Account(
Expand All @@ -446,7 +454,8 @@ def test_fill_london_blockchain_test_invalid_txs():
0xcccccccccccccccccccccccccccccccccccccccc,
0, 0, 0, 0)
}
"""
""",
fork=fork,
),
),
"0x000000000000000000000000000000000000c0de": Account(
Expand All @@ -459,7 +468,8 @@ def test_fill_london_blockchain_test_invalid_txs():
0xcccccccccccccccccccccccccccccccccccccccc,
0, 0, 0, 0)
}
"""
""",
fork=fork,
),
),
"0xccccccccccccccccccccccccccccccccccccccce": Account(
Expand All @@ -474,7 +484,8 @@ def test_fill_london_blockchain_test_invalid_txs():
0xcccccccccccccccccccccccccccccccccccccccc,
0, 0, 0, 0)
}
"""
""",
fork=fork,
),
),
}
Expand Down Expand Up @@ -719,11 +730,11 @@ def test_fill_london_blockchain_test_invalid_txs():
t8n = EvmTransitionTool()

fixture = {
f"000/my_blockchain_test/{London}": fill_test(
f"000/my_blockchain_test/{fork.name()}": fill_test(
t8n=t8n,
b11r=b11r,
test_spec=blockchain_test,
fork=London,
fork=fork,
engine="NoProof",
spec=None,
)
Expand Down
Loading
Loading