Skip to content

Commit

Permalink
Merge branch 'master' into refactor/effects-analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper authored Oct 5, 2024
2 parents 2efaf68 + 0e29db0 commit 53fa900
Show file tree
Hide file tree
Showing 18 changed files with 282 additions and 116 deletions.
69 changes: 39 additions & 30 deletions docs/installing-vyper.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,45 +7,24 @@ any errors.

.. note::

The easiest way to experiment with the language is to use the `Remix online compiler <https://remix.ethereum.org>`_.
(Activate the vyper-remix plugin in the Plugin manager.)
The easiest way to experiment with the language is to use either `Try Vyper! <https://try.vyperlang.org>`_ (maintained by the Vyper team) or the `Remix online compiler <https://remix.ethereum.org>`_ (maintained by the Ethereum Foundation).
- To use Try Vyper, go to https://try.vyperlang.org and log in (requires Github login).
- To use remix, go to https://remix.ethereum.org and activate the vyper-remix plugin in the Plugin manager.

Docker
******

Vyper can be downloaded as docker image from `dockerhub <https://hub.docker.com/r/vyperlang/vyper/tags?page=1&ordering=last_updated>`_:
::

docker pull vyperlang/vyper

To run the compiler use the ``docker run`` command:
::

docker run -v $(pwd):/code vyperlang/vyper /code/<contract_file.vy>

Alternatively you can log into the docker image and execute vyper on the prompt.
::

docker run -v $(pwd):/code/ -it --entrypoint /bin/bash vyperlang/vyper
root@d35252d1fb1b:/code# vyper <contract_file.vy>

The normal parameters are also supported, for example:
::
Binaries
********

docker run -v $(pwd):/code vyperlang/vyper -f abi /code/<contract_file.vy>
[{'name': 'test1', 'outputs': [], 'inputs': [{'type': 'uint256', 'name': 'a'}, {'type': 'bytes', 'name': 'b'}], 'constant': False, 'payable': False, 'type': 'function', 'gas': 441}, {'name': 'test2', 'outputs': [], 'inputs': [{'type': 'uint256', 'name': 'a'}], 'constant': False, 'payable': False, 'type': 'function', 'gas': 316}]
Vyper binaries for Windows, Mac and Linux are available for download from the `GitHub releases page
<https://github.com/vyperlang/vyper/releases>`_.

.. note::

If you would like to know how to install Docker, please follow their `documentation <https://docs.docker.com/get-docker/>`_.

PIP
***

Installing Python
=================

Vyper can only be built using Python 3.6 and higher. If you need to know how to install the correct version of python,
Vyper can only be built using Python 3.10 and higher. If you need to know how to install the correct version of python,
follow the instructions from the official `Python website <https://wiki.python.org/moin/BeginnersGuide/Download>`_.

Creating a virtual environment
Expand Down Expand Up @@ -76,13 +55,43 @@ Each tagged version of vyper is uploaded to `pypi <https://pypi.org/project/vype
To install a specific version use:
::

pip install vyper==0.3.7
pip install vyper==0.4.0

You can check if Vyper is installed completely or not by typing the following in your terminal/cmd:
::

vyper --version


Docker
******

Vyper can be downloaded as docker image from `dockerhub <https://hub.docker.com/r/vyperlang/vyper/tags?page=1&ordering=last_updated>`_:
::

docker pull vyperlang/vyper

To run the compiler use the ``docker run`` command:
::

docker run -v $(pwd):/code vyperlang/vyper /code/<contract_file.vy>

Alternatively you can log into the docker image and execute vyper on the prompt.
::

docker run -v $(pwd):/code/ -it --entrypoint /bin/bash vyperlang/vyper
root@d35252d1fb1b:/code# vyper <contract_file.vy>

The normal parameters are also supported, for example:
::

docker run -v $(pwd):/code vyperlang/vyper -f abi /code/<contract_file.vy>
[{'name': 'test1', 'outputs': [], 'inputs': [{'type': 'uint256', 'name': 'a'}, {'type': 'bytes', 'name': 'b'}], 'constant': False, 'payable': False, 'type': 'function', 'gas': 441}, {'name': 'test2', 'outputs': [], 'inputs': [{'type': 'uint256', 'name': 'a'}], 'constant': False, 'payable': False, 'type': 'function', 'gas': 316}]

.. note::

If you would like to know how to install Docker, please follow their `documentation <https://docs.docker.com/get-docker/>`_.

nix
***

Expand Down
18 changes: 16 additions & 2 deletions tests/functional/builtins/codegen/test_raw_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ def __default__():
assert env.message_call(caller.address, data=sig) == b""


def _strip_initcode_suffix(bytecode):
bs = bytes.fromhex(bytecode.removeprefix("0x"))
to_strip = int.from_bytes(bs[-2:], "big")
return bs[:-to_strip].hex()


# check max_outsize=0 does same thing as not setting max_outsize.
# compile to bytecode and compare bytecode directly.
def test_max_outsize_0():
Expand All @@ -276,7 +282,11 @@ def test_raw_call(_target: address):
"""
output1 = compile_code(code1, output_formats=["bytecode", "bytecode_runtime"])
output2 = compile_code(code2, output_formats=["bytecode", "bytecode_runtime"])
assert output1 == output2
assert output1["bytecode_runtime"] == output2["bytecode_runtime"]

bytecode1 = output1["bytecode"]
bytecode2 = output2["bytecode"]
assert _strip_initcode_suffix(bytecode1) == _strip_initcode_suffix(bytecode2)


# check max_outsize=0 does same thing as not setting max_outsize,
Expand All @@ -298,7 +308,11 @@ def test_raw_call(_target: address) -> bool:
"""
output1 = compile_code(code1, output_formats=["bytecode", "bytecode_runtime"])
output2 = compile_code(code2, output_formats=["bytecode", "bytecode_runtime"])
assert output1 == output2
assert output1["bytecode_runtime"] == output2["bytecode_runtime"]

bytecode1 = output1["bytecode"]
bytecode2 = output2["bytecode"]
assert _strip_initcode_suffix(bytecode1) == _strip_initcode_suffix(bytecode2)


# test functionality of max_outsize=0
Expand Down
37 changes: 28 additions & 9 deletions tests/unit/compiler/test_bytecode_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,17 @@ def test_bytecode_runtime():


def test_bytecode_signature():
out = vyper.compile_code(simple_contract_code, output_formats=["bytecode_runtime", "bytecode"])
out = vyper.compile_code(
simple_contract_code, output_formats=["bytecode_runtime", "bytecode", "integrity"]
)

runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))

metadata = _parse_cbor_metadata(initcode)
runtime_len, data_section_lengths, immutables_len, compiler = metadata
integrity_hash, runtime_len, data_section_lengths, immutables_len, compiler = metadata

assert integrity_hash.hex() == out["integrity"]

assert runtime_len == len(runtime_code)
assert data_section_lengths == []
Expand All @@ -73,14 +77,18 @@ def test_bytecode_signature_dense_jumptable():
settings = Settings(optimize=OptimizationLevel.CODESIZE)

out = vyper.compile_code(
many_functions, output_formats=["bytecode_runtime", "bytecode"], settings=settings
many_functions,
output_formats=["bytecode_runtime", "bytecode", "integrity"],
settings=settings,
)

runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))

metadata = _parse_cbor_metadata(initcode)
runtime_len, data_section_lengths, immutables_len, compiler = metadata
integrity_hash, runtime_len, data_section_lengths, immutables_len, compiler = metadata

assert integrity_hash.hex() == out["integrity"]

assert runtime_len == len(runtime_code)
assert data_section_lengths == [5, 35]
Expand All @@ -92,14 +100,18 @@ def test_bytecode_signature_sparse_jumptable():
settings = Settings(optimize=OptimizationLevel.GAS)

out = vyper.compile_code(
many_functions, output_formats=["bytecode_runtime", "bytecode"], settings=settings
many_functions,
output_formats=["bytecode_runtime", "bytecode", "integrity"],
settings=settings,
)

runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))

metadata = _parse_cbor_metadata(initcode)
runtime_len, data_section_lengths, immutables_len, compiler = metadata
integrity_hash, runtime_len, data_section_lengths, immutables_len, compiler = metadata

assert integrity_hash.hex() == out["integrity"]

assert runtime_len == len(runtime_code)
assert data_section_lengths == [8]
Expand All @@ -108,13 +120,17 @@ def test_bytecode_signature_sparse_jumptable():


def test_bytecode_signature_immutables():
out = vyper.compile_code(has_immutables, output_formats=["bytecode_runtime", "bytecode"])
out = vyper.compile_code(
has_immutables, output_formats=["bytecode_runtime", "bytecode", "integrity"]
)

runtime_code = bytes.fromhex(out["bytecode_runtime"].removeprefix("0x"))
initcode = bytes.fromhex(out["bytecode"].removeprefix("0x"))

metadata = _parse_cbor_metadata(initcode)
runtime_len, data_section_lengths, immutables_len, compiler = metadata
integrity_hash, runtime_len, data_section_lengths, immutables_len, compiler = metadata

assert integrity_hash.hex() == out["integrity"]

assert runtime_len == len(runtime_code)
assert data_section_lengths == []
Expand All @@ -129,7 +145,10 @@ def test_bytecode_signature_deployed(code, get_contract, env):
deployed_code = env.get_code(c.address)

metadata = _parse_cbor_metadata(c.bytecode)
runtime_len, data_section_lengths, immutables_len, compiler = metadata
integrity_hash, runtime_len, data_section_lengths, immutables_len, compiler = metadata

out = vyper.compile_code(code, output_formats=["integrity"])
assert integrity_hash.hex() == out["integrity"]

assert compiler == {"vyper": list(vyper.version.version_tuple)}

Expand Down
12 changes: 9 additions & 3 deletions tests/unit/compiler/venom/test_duplicate_operands.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from vyper.compiler.settings import OptimizationLevel
from vyper.venom import generate_assembly_experimental
from vyper.venom.analysis.analysis import IRAnalysesCache
from vyper.venom.context import IRContext
from vyper.venom.passes.store_expansion import StoreExpansionPass


def test_duplicate_operands():
Expand All @@ -13,7 +15,7 @@ def test_duplicate_operands():
%3 = mul %1, %2
stop
Should compile to: [PUSH1, 10, DUP1, DUP1, DUP1, ADD, MUL, POP, STOP]
Should compile to: [PUSH1, 10, DUP1, DUP2, ADD, MUL, POP, STOP]
"""
ctx = IRContext()
fn = ctx.create_function("test")
Expand All @@ -23,5 +25,9 @@ def test_duplicate_operands():
bb.append_instruction("mul", sum_, op)
bb.append_instruction("stop")

asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.GAS)
assert asm == ["PUSH1", 10, "DUP1", "DUP1", "ADD", "MUL", "POP", "STOP"]
ac = IRAnalysesCache(fn)
StoreExpansionPass(ac, fn).run_pass()

optimize = OptimizationLevel.GAS
asm = generate_assembly_experimental(ctx, optimize=optimize)
assert asm == ["PUSH1", 10, "DUP1", "DUP2", "ADD", "MUL", "POP", "STOP"]
3 changes: 2 additions & 1 deletion tests/unit/compiler/venom/test_stack_cleanup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ def test_cleanup_stack():
bb = fn.get_basic_block()
ret_val = bb.append_instruction("param")
op = bb.append_instruction("store", 10)
bb.append_instruction("add", op, op)
op2 = bb.append_instruction("store", op)
bb.append_instruction("add", op, op2)
bb.append_instruction("ret", ret_val)

asm = generate_assembly_experimental(ctx, optimize=OptimizationLevel.GAS)
Expand Down
33 changes: 33 additions & 0 deletions tests/unit/compiler/venom/test_stack_reorder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
from vyper.venom import generate_assembly_experimental
from vyper.venom.analysis.analysis import IRAnalysesCache
from vyper.venom.context import IRContext
from vyper.venom.passes.store_expansion import StoreExpansionPass


def test_stack_reorder():
"""
Test to was created from the example in the
issue https://github.com/vyperlang/vyper/issues/4215
this example should fail with original stack reorder
algorithm but succeed with new one
"""
ctx = IRContext()
fn = ctx.create_function("_global")

bb = fn.get_basic_block()
var0 = bb.append_instruction("store", 1)
var1 = bb.append_instruction("store", 2)
var2 = bb.append_instruction("store", 3)
var3 = bb.append_instruction("store", 4)
var4 = bb.append_instruction("store", 5)

bb.append_instruction("staticcall", var0, var1, var2, var3, var4, var3)

ret_val = bb.append_instruction("add", var4, var4)

bb.append_instruction("ret", ret_val)

ac = IRAnalysesCache(fn)
StoreExpansionPass(ac, fn).run_pass()

generate_assembly_experimental(ctx)
6 changes: 2 additions & 4 deletions vyper/compiler/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,15 +320,13 @@ def _build_source_map_output(compiler_data, bytecode, pc_maps):


def build_source_map_output(compiler_data: CompilerData) -> dict:
bytecode, pc_maps = compile_ir.assembly_to_evm(
compiler_data.assembly, insert_compiler_metadata=False
)
bytecode, pc_maps = compile_ir.assembly_to_evm(compiler_data.assembly, compiler_metadata=None)
return _build_source_map_output(compiler_data, bytecode, pc_maps)


def build_source_map_runtime_output(compiler_data: CompilerData) -> dict:
bytecode, pc_maps = compile_ir.assembly_to_evm(
compiler_data.assembly_runtime, insert_compiler_metadata=False
compiler_data.assembly_runtime, compiler_metadata=None
)
return _build_source_map_output(compiler_data, bytecode, pc_maps)

Expand Down
9 changes: 4 additions & 5 deletions vyper/compiler/output_bundle.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import importlib
import io
import json
import os
import zipfile
from dataclasses import dataclass
from functools import cached_property
Expand All @@ -13,7 +12,7 @@
from vyper.compiler.settings import Settings
from vyper.exceptions import CompilerPanic
from vyper.semantics.analysis.module import _is_builtin
from vyper.utils import get_long_version
from vyper.utils import get_long_version, safe_relpath

# data structures and routines for constructing "output bundles",
# basically reproducible builds of a vyper contract, with varying
Expand Down Expand Up @@ -62,7 +61,7 @@ def compiler_inputs(self) -> dict[str, CompilerInput]:

sources = {}
for c in inputs:
path = os.path.relpath(c.resolved_path)
path = safe_relpath(c.resolved_path)
# note: there should be a 1:1 correspondence between
# resolved_path and source_id, but for clarity use resolved_path
# since it corresponds more directly to search path semantics.
Expand All @@ -73,7 +72,7 @@ def compiler_inputs(self) -> dict[str, CompilerInput]:
@cached_property
def compilation_target_path(self):
p = PurePath(self.compiler_data.file_input.resolved_path)
p = os.path.relpath(p)
p = safe_relpath(p)
return _anonymize(p)

@cached_property
Expand Down Expand Up @@ -121,7 +120,7 @@ def used_search_paths(self) -> list[str]:
sps = [sp for sp, count in tmp.items() if count > 0]
assert len(sps) > 0

return [_anonymize(os.path.relpath(sp)) for sp in sps]
return [_anonymize(safe_relpath(sp)) for sp in sps]


class OutputBundleWriter:
Expand Down
Loading

0 comments on commit 53fa900

Please sign in to comment.