From 9fef4f51952d7907d471ef224a71304c918deaa3 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sun, 19 Jan 2025 14:42:06 +0000 Subject: [PATCH 01/17] Rebuilt for https://fedoraproject.org/wiki/Fedora_42_Mass_Rebuild From 870f249ed6848f8e73f7e4238c9a622a34eb0fa2 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Wed, 8 Jan 2025 02:38:40 +0100 Subject: [PATCH 02/17] Fix few issues Signed-off-by: Peter Lemenkov --- ...g-define-rounding-mode-for-sqrt-4486.patch | 51 +++++ ...degen-cache-result-of-iter-eval-4488.patch | 73 ++++++ ...-disable-augassign-with-overlap-4487.patch | 210 ++++++++++++++++++ vyper.spec | 13 +- 4 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch create mode 100644 vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch create mode 100644 vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch diff --git a/vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch b/vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch new file mode 100644 index 0000000..841c453 --- /dev/null +++ b/vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch @@ -0,0 +1,51 @@ +From: Charles Cooper +Date: Sun, 23 Feb 2025 10:38:22 +0100 +Subject: [PATCH] fix[lang]: define rounding mode for sqrt (#4486) + +prior to this commit, the rounding mode for `sqrt()` is undefined, +which could be an issue for applications which use `sqrt()` to +determine boundary conditions. this commit ensures the result is +rounded down. + +diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst +index a0e424ad..6ac659bd 100644 +--- a/docs/built-in-functions.rst ++++ b/docs/built-in-functions.rst +@@ -711,7 +711,7 @@ Math + + .. py:function:: sqrt(d: decimal) -> decimal + +- Return the square root of the provided decimal number, using the Babylonian square root algorithm. ++ Return the square root of the provided decimal number, using the Babylonian square root algorithm. The rounding mode is to round down to the nearest epsilon. For instance, ``sqrt(0.9999999998) == 0.9999999998``. + + .. code-block:: vyper + +diff --git a/tests/functional/codegen/types/numbers/test_sqrt.py b/tests/functional/codegen/types/numbers/test_sqrt.py +index cf62cecd..54b762c4 100644 +--- a/tests/functional/codegen/types/numbers/test_sqrt.py ++++ b/tests/functional/codegen/types/numbers/test_sqrt.py +@@ -146,6 +146,10 @@ def test_sqrt_bounds(sqrt_contract, value): + ) + @hypothesis.example(value=Decimal(SizeLimits.MAX_INT128)) + @hypothesis.example(value=Decimal(0)) ++# cf. GHSA-2p94-8669-xg86 for the following three examples: ++@hypothesis.example(value=Decimal("0.9999999998")) ++@hypothesis.example(value=Decimal("0.9999999997")) ++@hypothesis.example(value=Decimal("1.1000000000")) + def test_sqrt_valid_range(sqrt_contract, value): + vyper_sqrt = sqrt_contract.test(decimal_to_int(value)) + actual_sqrt = decimal_sqrt(value) +diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py +index 672d9784..5c7e3a6a 100644 +--- a/vyper/builtins/functions.py ++++ b/vyper/builtins/functions.py +@@ -2142,6 +2142,9 @@ else: + break + y = z + z = (x / z + z) / 2.0 ++ ++ if y < z: ++ z = y + """ + + x_type = DecimalT() diff --git a/vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch b/vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch new file mode 100644 index 0000000..1bdc18b --- /dev/null +++ b/vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch @@ -0,0 +1,73 @@ +From: Charles Cooper +Date: Sat, 22 Feb 2025 19:34:41 +0100 +Subject: [PATCH] fix[codegen]: cache result of iter eval (#4488) + +prior to this commit, multiple evaluation of a single expression is +possible in the iterator target of a for loop. while the iterator +expression cannot produce multiple writes, it can _consume_ side +effects produced in the loop body (e.g. read a storage variable +updated in the loop body) and thus lead to unexpected program +behavior. specifically, reads in iterators which contain an `IfExp` +(e.g. `for s: uint256 in ([read(), read()] if True else []))` would +issue one evaluation of the list `[read(), read()]` per loop iteration, +thus interleaving reads with writes in the loop body. + +this commit fixes the issue by using `cache_when_complex` to enforce +evaluation of the iterator before entering the loop body. + +this is incidentally also a performance fix, since it moves the +repeated evaluation into a one-time evaluation. + +references: +- https://github.com/vyperlang/vyper/security/advisories/GHSA-h33q-mhmp-8p67 + +diff --git a/vyper/codegen/stmt.py b/vyper/codegen/stmt.py +index 830f2f92..165252d5 100644 +--- a/vyper/codegen/stmt.py ++++ b/vyper/codegen/stmt.py +@@ -267,20 +267,21 @@ class Stmt: + ret.append(make_setter(tmp_list, iter_list)) + iter_list = tmp_list + +- # set up the loop variable +- e = get_element_ptr(iter_list, i, array_bounds_check=False) +- body = ["seq", make_setter(loop_var, e), parse_body(self.stmt.body, self.context)] +- +- repeat_bound = iter_list.typ.count +- if isinstance(iter_list.typ, DArrayT): +- array_len = get_dyn_array_count(iter_list) +- else: +- array_len = repeat_bound ++ with iter_list.cache_when_complex("list_iter") as (b1, iter_list): ++ # set up the loop variable ++ e = get_element_ptr(iter_list, i, array_bounds_check=False) ++ body = ["seq", make_setter(loop_var, e), parse_body(self.stmt.body, self.context)] ++ ++ repeat_bound = iter_list.typ.count ++ if isinstance(iter_list.typ, DArrayT): ++ array_len = get_dyn_array_count(iter_list) ++ else: ++ array_len = repeat_bound + +- ret.append(["repeat", i, 0, array_len, repeat_bound, body]) ++ ret.append(["repeat", i, 0, array_len, repeat_bound, body]) + +- del self.context.forvars[varname] +- return IRnode.from_list(ret) ++ del self.context.forvars[varname] ++ return b1.resolve(IRnode.from_list(ret)) + + def parse_AugAssign(self): + target = self._get_target(self.stmt.target) +diff --git a/vyper/semantics/analysis/local.py b/vyper/semantics/analysis/local.py +index 26c6a4ef..909f5b7b 100644 +--- a/vyper/semantics/analysis/local.py ++++ b/vyper/semantics/analysis/local.py +@@ -522,6 +522,7 @@ class FunctionAnalyzer(VyperNodeVisitorBase): + def _analyse_range_iter(self, iter_node, target_type): + # iteration via range() + if iter_node.get("func.id") != "range": ++ # CMC 2025-02-12 I think we can allow this actually + raise IteratorException("Cannot iterate over the result of a function call", iter_node) + _validate_range_call(iter_node) + diff --git a/vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch b/vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch new file mode 100644 index 0000000..6665af3 --- /dev/null +++ b/vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch @@ -0,0 +1,210 @@ +From: Charles Cooper +Date: Sun, 23 Feb 2025 20:51:52 +0100 +Subject: [PATCH] fix[codegen]: disable augassign with overlap (#4487) + +in vyper, the behavior for AugAssign is to perform the bounds checks +only before evaluation of the rhs, rather than before-and-after. in +other words, the following code: + +```vyper +def poc(): + a: DynArray[uint256, 2] = [1, 2] + a[1] += a.pop() +``` + +is equivalent to: + +```vyper +def poc(): + a: DynArray[uint256, 2] = [1, 2] + a[1] += a[len(a) - 1] + a.pop() +``` + +rather than: + +```vyper +def poc(): + a: DynArray[uint256, 2] = [1, 2] + s: uint256 = a[1] + t: uint256 = a.pop() + a[1] = s + t # reverts due to oob access +``` + +this commit blocks the potentially missing bounds check by panicking +when there is a potential write on the rhs of an AugAssign which could +change the length on the lhs. + +references: +- https://github.com/vyperlang/vyper/security/advisories/GHSA-4w26-8p97-f4jp + +--------- + +Co-authored-by: cyberthirst + +diff --git a/tests/functional/codegen/features/test_assignment.py b/tests/functional/codegen/features/test_assignment.py +index eaafb527..53d02dfb 100644 +--- a/tests/functional/codegen/features/test_assignment.py ++++ b/tests/functional/codegen/features/test_assignment.py +@@ -1,6 +1,6 @@ + import pytest + +-from vyper.exceptions import ImmutableViolation, InvalidType, TypeMismatch ++from vyper.exceptions import CodegenPanic, ImmutableViolation, InvalidType, TypeMismatch + + + def test_augassign(get_contract): +@@ -39,6 +39,135 @@ def augmod(x: int128, y: int128) -> int128: + print("Passed aug-assignment test") + + ++@pytest.mark.parametrize( ++ "source", ++ [ ++ """ ++@external ++def poc(): ++ a: DynArray[uint256, 2] = [1, 2] ++ a[1] += a.pop() ++ """, ++ """ ++a: DynArray[uint256, 2] ++ ++def side_effect() -> uint256: ++ return self.a.pop() ++ ++@external ++def poc(): ++ self.a = [1, 2] ++ self.a[1] += self.side_effect() ++ """, ++ """ ++a: DynArray[uint256, 2] ++ ++def side_effect() -> uint256: ++ self.a = [1] ++ return 1 ++ ++@external ++def poc(): ++ self.a = [1, 2] ++ self.a[1] += self.side_effect() ++ """, ++ """ ++a: DynArray[uint256, 2] ++ ++interface Foo: ++ def foo() -> uint256: nonpayable ++ ++@external ++def foo() -> uint256: ++ return self.a.pop() ++ ++@external ++def poc(): ++ self.a = [1, 2] ++ # panics due to extcall ++ self.a[1] += extcall Foo(self).foo() ++ """, ++ ], ++) ++@pytest.mark.xfail(strict=True, raises=CodegenPanic) ++def test_augassign_oob(get_contract, tx_failed, source): ++ # xfail here (with panic): ++ c = get_contract(source) ++ ++ # not reached until the panic is fixed ++ with tx_failed(c): ++ c.poc() ++ ++ ++@pytest.mark.parametrize( ++ "source", ++ [ ++ """ ++a: public(DynArray[uint256, 2]) ++ ++interface Foo: ++ def foo() -> uint256: view ++ ++@external ++def foo() -> uint256: ++ return self.a[1] ++ ++@external ++def entry() -> DynArray[uint256, 2]: ++ self.a = [1, 1] ++ # panics due to staticcall ++ self.a[1] += staticcall Foo(self).foo() ++ return self.a ++ """ ++ ], ++) ++@pytest.mark.xfail(strict=True, raises=CodegenPanic) ++def test_augassign_rhs_references_lhs(get_contract, tx_failed, source): ++ # xfail here (with panic): ++ c = get_contract(source) ++ ++ assert c.entry() == [1, 2] ++ ++ ++@pytest.mark.parametrize( ++ "source", ++ [ ++ """ ++@external ++def entry() -> DynArray[uint256, 2]: ++ a: DynArray[uint256, 2] = [1, 1] ++ a[1] += a[1] ++ return a ++ """, ++ """ ++@external ++def entry() -> DynArray[uint256, 2]: ++ a: uint256 = 1 ++ a += a ++ b: DynArray[uint256, 2] = [a, a] ++ b[0] -= b[0] ++ b[0] += b[1] // 2 ++ return b ++ """, ++ """ ++a: DynArray[uint256, 2] ++ ++def read() -> uint256: ++ return self.a[1] ++ ++@external ++def entry() -> DynArray[uint256, 2]: ++ self.a = [1, 1] ++ self.a[1] += self.read() ++ return self.a ++ """, ++ ], ++) ++def test_augassign_rhs_references_lhs2(get_contract, source): ++ c = get_contract(source) ++ assert c.entry() == [1, 2] ++ ++ + @pytest.mark.parametrize( + "typ,in_val,out_val", + [ +diff --git a/vyper/codegen/stmt.py b/vyper/codegen/stmt.py +index 165252d5..24391e27 100644 +--- a/vyper/codegen/stmt.py ++++ b/vyper/codegen/stmt.py +@@ -293,6 +293,13 @@ class Stmt: + # single word load/stores are atomic. + raise TypeCheckFailure("unreachable") + ++ for var in target.referenced_variables: ++ if var.typ._is_prim_word: ++ continue ++ # oob - GHSA-4w26-8p97-f4jp ++ if var in right.variable_writes or right.contains_risky_call: ++ raise CodegenPanic("unreachable") ++ + with target.cache_when_complex("_loc") as (b, target): + left = IRnode.from_list(LOAD(target), typ=target.typ) + new_val = Expr.handle_binop(self.stmt.op, left, right, self.context) diff --git a/vyper.spec b/vyper.spec index 52e148c..3e429be 100644 --- a/vyper.spec +++ b/vyper.spec @@ -13,8 +13,18 @@ Patch2: vyper-0002-Ease-version-requirements.patch Patch3: vyper-0003-Lark-should-go-to-the-main-install-section-as-it-use.patch Patch4: vyper-0004-Remove-unnecessary-shebang.patch Patch5: vyper-0005-Relax-lark-requirement-testing-only-anyway.patch +Patch6: vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch +Patch7: vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch +Patch8: vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch BuildRequires: git +BuildRequires: python3-cached_property BuildRequires: python3-devel +BuildRequires: python3-eth-abi +BuildRequires: python3-eth-keys +BuildRequires: python3-eth-stdlib +BuildRequires: python3-hypothesis +BuildRequires: python3-pytest +BuildRequires: python3-rlp %description %{summary}. @@ -36,7 +46,8 @@ rm -f %{buildroot}/usr/vyper_git_commithash.txt %check %pyproject_check_import -# FIXME not enough dependencies +# FIXME requires https://github.com/ethereum/py-evm +# FIXME requires https://github.com/paradigmxyz/pyrevm #%%pytest %files -f %{pyproject_files} From b66de710223316c8cb3c661d03bed2e5a9f74e43 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Thu, 27 Feb 2025 15:52:15 +0100 Subject: [PATCH 03/17] Fix for one more issue Signed-off-by: Peter Lemenkov --- ...assertions-for-certain-precompiles-4.patch | 404 ++++++++++++++++++ vyper.spec | 1 + 2 files changed, 405 insertions(+) create mode 100644 vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch diff --git a/vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch b/vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch new file mode 100644 index 0000000..49afa86 --- /dev/null +++ b/vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch @@ -0,0 +1,404 @@ +From: Charles Cooper +Date: Mon, 20 Jan 2025 11:51:21 -0500 +Subject: [PATCH] fix[codegen]: fix assertions for certain precompiles (#4451) + +this commit fixes a flaw in code generation for certain +precompiles. specifically, some calls to the ecrecover (0x01) and +identity (0x04) precompiles were not checked for success. + +in 93a957947af1088addc, the assert for memory copying calls to the +identity precompile was optimized out; the reasoning being that if the +identity precompile fails due to OOG, the contract would also likely +fail with OOG. however, due to the 63/64ths rule, there are cases where +just enough gas was supplied to the current call context so that the +subcall to the precompile could fail with OOG, but the contract has +enough gas to continue execution after it shouldn't (which is undefined +behavior) and then successfully return out of the call context. + +(note that even prior to 93a957947af1088addc, some calls to the +identity precompile did not check the success flag. cf. commit +cf03d27be6a74c0c33de. the call to ecrecover was unchecked since +inception - db44cde626919ed8bebf). + +note also that since cancun, memory copies are implemented using +the `mcopy` instruction, so the bug as it pertains to the identity +precompile only affects pre-cancun compilation targets. + +this commit fixes the flaw by converting the relevant unchecked calls +to checked calls. + +it also adds tests that trigger the behavior by running the call, and +then performing the exact same call again but providing `gas_used` back +to the contract, which is the minimum amount of gas for the call to the +contract to finish execution. the specific amount of gas left at the +point of the subcall is small enough to cause the subcall to fail (and +the check around the subcall success to revert, which is what is tested +for in the new tests). in these tests, it also adds a static check +that the IR is well-formed (that all relevant calls to precompiles are +appropriately checked). + +references: +- https://github.com/vyperlang/vyper/security/advisories/GHSA-vgf2-gvx8-xwc3 + +diff --git a/tests/functional/builtins/codegen/test_ecrecover.py b/tests/functional/builtins/codegen/test_ecrecover.py +index 8db51fdd..47a22506 100644 +--- a/tests/functional/builtins/codegen/test_ecrecover.py ++++ b/tests/functional/builtins/codegen/test_ecrecover.py +@@ -1,7 +1,10 @@ ++import contextlib ++ + from eth_account import Account + from eth_account._utils.signing import to_bytes32 + +-from tests.utils import ZERO_ADDRESS ++from tests.utils import ZERO_ADDRESS, check_precompile_asserts ++from vyper.compiler.settings import OptimizationLevel + + + def test_ecrecover_test(get_contract): +@@ -86,3 +89,40 @@ def test_ecrecover() -> bool: + """ + c = get_contract(code) + assert c.test_ecrecover() is True ++ ++ ++def test_ecrecover_oog_handling(env, get_contract, tx_failed, optimize, experimental_codegen): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++@external ++@view ++def do_ecrecover(hash: bytes32, v: uint256, r:uint256, s:uint256) -> address: ++ return ecrecover(hash, v, r, s) ++ """ ++ check_precompile_asserts(code) ++ ++ c = get_contract(code) ++ ++ h = b"\x35" * 32 ++ local_account = Account.from_key(b"\x46" * 32) ++ sig = local_account.signHash(h) ++ v, r, s = sig.v, sig.r, sig.s ++ ++ assert c.do_ecrecover(h, v, r, s) == local_account.address ++ ++ gas_used = env.last_result.gas_used ++ ++ if optimize == OptimizationLevel.NONE and not experimental_codegen: ++ # if optimizations are off, enough gas is used by the contract ++ # that the gas provided to ecrecover (63/64ths rule) is enough ++ # for it to succeed ++ ctx = contextlib.nullcontext ++ else: ++ # in other cases, the gas forwarded is small enough for ecrecover ++ # to fail with oog, which we handle by reverting. ++ ctx = tx_failed ++ ++ with ctx(): ++ # provide enough spare gas for the top-level call to not oog but ++ # not enough for ecrecover to succeed ++ c.do_ecrecover(h, v, r, s, gas=gas_used) +diff --git a/tests/functional/codegen/types/test_dynamic_array.py b/tests/functional/codegen/types/test_dynamic_array.py +index 2a0f4e77..b5247efb 100644 +--- a/tests/functional/codegen/types/test_dynamic_array.py ++++ b/tests/functional/codegen/types/test_dynamic_array.py +@@ -1,10 +1,12 @@ ++import contextlib + import itertools + from typing import Any, Callable + + import pytest + +-from tests.utils import decimal_to_int ++from tests.utils import check_precompile_asserts, decimal_to_int + from vyper.compiler import compile_code ++from vyper.evm.opcodes import version_check + from vyper.exceptions import ( + ArgumentException, + ArrayIndexException, +@@ -1903,3 +1905,59 @@ def foo(): + c = get_contract(code) + with tx_failed(): + c.foo() ++ ++ ++def test_dynarray_copy_oog(env, get_contract, tx_failed): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++ ++@external ++def foo(a: DynArray[uint256, 4000]) -> uint256: ++ b: DynArray[uint256, 4000] = a ++ return b[0] ++ """ ++ check_precompile_asserts(code) ++ ++ c = get_contract(code) ++ dynarray = [2] * 4000 ++ assert c.foo(dynarray) == 2 ++ ++ gas_used = env.last_result.gas_used ++ if version_check(begin="cancun"): ++ ctx = contextlib.nullcontext ++ else: ++ ctx = tx_failed ++ ++ with ctx(): ++ # depends on EVM version. pre-cancun, will revert due to checking ++ # success flag from identity precompile. ++ c.foo(dynarray, gas=gas_used) ++ ++ ++def test_dynarray_copy_oog2(env, get_contract, tx_failed): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++@external ++@view ++def foo(x: String[1000000], y: String[1000000]) -> DynArray[String[1000000], 2]: ++ z: DynArray[String[1000000], 2] = [x, y] ++ # Some code ++ return z ++ """ ++ check_precompile_asserts(code) ++ ++ c = get_contract(code) ++ calldata0 = "a" * 10 ++ calldata1 = "b" * 1000000 ++ assert c.foo(calldata0, calldata1) == [calldata0, calldata1] ++ ++ gas_used = env.last_result.gas_used ++ if version_check(begin="cancun"): ++ ctx = contextlib.nullcontext ++ else: ++ ctx = tx_failed ++ ++ with ctx(): ++ # depends on EVM version. pre-cancun, will revert due to checking ++ # success flag from identity precompile. ++ c.foo(calldata0, calldata1, gas=gas_used) +diff --git a/tests/functional/codegen/types/test_lists.py b/tests/functional/codegen/types/test_lists.py +index 953a9a9f..26cd16ed 100644 +--- a/tests/functional/codegen/types/test_lists.py ++++ b/tests/functional/codegen/types/test_lists.py +@@ -1,8 +1,12 @@ ++import contextlib + import itertools + + import pytest + +-from tests.utils import decimal_to_int ++from tests.evm_backends.base_env import EvmError ++from tests.utils import check_precompile_asserts, decimal_to_int ++from vyper.compiler.settings import OptimizationLevel ++from vyper.evm.opcodes import version_check + from vyper.exceptions import ArrayIndexException, OverflowException, TypeMismatch + + +@@ -848,3 +852,73 @@ def foo() -> {return_type}: + return MY_CONSTANT[0][0] + """ + assert_compile_failed(lambda: get_contract(code), TypeMismatch) ++ ++ ++def test_array_copy_oog(env, get_contract, tx_failed, optimize, experimental_codegen, request): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++@internal ++def bar(x: uint256[3000]) -> uint256[3000]: ++ a: uint256[3000] = x ++ return a ++ ++@external ++def foo(x: uint256[3000]) -> uint256: ++ s: uint256[3000] = self.bar(x) ++ return s[0] ++ """ ++ check_precompile_asserts(code) ++ ++ if optimize == OptimizationLevel.NONE and not experimental_codegen: ++ # fails in bytecode generation due to jumpdests too large ++ with pytest.raises(AssertionError): ++ get_contract(code) ++ return ++ ++ c = get_contract(code) ++ array = [2] * 3000 ++ assert c.foo(array) == array[0] ++ ++ # get the minimum gas for the contract complete execution ++ gas_used = env.last_result.gas_used ++ if version_check(begin="cancun"): ++ ctx = contextlib.nullcontext ++ else: ++ ctx = tx_failed ++ with ctx(): ++ # depends on EVM version. pre-cancun, will revert due to checking ++ # success flag from identity precompile. ++ c.foo(array, gas=gas_used) ++ ++ ++def test_array_copy_oog2(env, get_contract, tx_failed, optimize, experimental_codegen, request): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++@external ++def foo(x: uint256[2500]) -> uint256: ++ s: uint256[2500] = x ++ t: uint256[2500] = s ++ return t[0] ++ """ ++ check_precompile_asserts(code) ++ ++ if optimize == OptimizationLevel.NONE and not experimental_codegen: ++ # fails in creating contract due to code too large ++ with tx_failed(EvmError): ++ get_contract(code) ++ return ++ ++ c = get_contract(code) ++ array = [2] * 2500 ++ assert c.foo(array) == array[0] ++ ++ # get the minimum gas for the contract complete execution ++ gas_used = env.last_result.gas_used ++ if version_check(begin="cancun"): ++ ctx = contextlib.nullcontext ++ else: ++ ctx = tx_failed ++ with ctx(): ++ # depends on EVM version. pre-cancun, will revert due to checking ++ # success flag from identity precompile. ++ c.foo(array, gas=gas_used) +diff --git a/tests/functional/codegen/types/test_string.py b/tests/functional/codegen/types/test_string.py +index 51899b50..2e340e8f 100644 +--- a/tests/functional/codegen/types/test_string.py ++++ b/tests/functional/codegen/types/test_string.py +@@ -1,5 +1,10 @@ ++import contextlib ++ + import pytest + ++from tests.utils import check_precompile_asserts ++from vyper.evm.opcodes import version_check ++ + + def test_string_return(get_contract): + code = """ +@@ -359,3 +364,56 @@ def compare_var_storage_not_equal_false() -> bool: + assert c.compare_var_storage_equal_false() is False + assert c.compare_var_storage_not_equal_true() is True + assert c.compare_var_storage_not_equal_false() is False ++ ++ ++def test_string_copy_oog(env, get_contract, tx_failed): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++@external ++@view ++def foo(x: String[1000000]) -> String[1000000]: ++ return x ++ """ ++ check_precompile_asserts(code) ++ ++ c = get_contract(code) ++ calldata = "a" * 1000000 ++ assert c.foo(calldata) == calldata ++ ++ gas_used = env.last_result.gas_used ++ if version_check(begin="cancun"): ++ ctx = contextlib.nullcontext ++ else: ++ ctx = tx_failed ++ ++ with ctx(): ++ # depends on EVM version. pre-cancun, will revert due to checking ++ # success flag from identity precompile. ++ c.foo(calldata, gas=gas_used) ++ ++ ++def test_string_copy_oog2(env, get_contract, tx_failed): ++ # GHSA-vgf2-gvx8-xwc3 ++ code = """ ++@external ++@view ++def foo(x: String[1000000]) -> uint256: ++ y: String[1000000] = x ++ return len(y) ++ """ ++ check_precompile_asserts(code) ++ ++ c = get_contract(code) ++ calldata = "a" * 1000000 ++ assert c.foo(calldata) == len(calldata) ++ ++ gas_used = env.last_result.gas_used ++ if version_check(begin="cancun"): ++ ctx = contextlib.nullcontext ++ else: ++ ctx = tx_failed ++ ++ with ctx(): ++ # depends on EVM version. pre-cancun, will revert due to checking ++ # success flag from identity precompile. ++ c.foo(calldata, gas=gas_used) +diff --git a/tests/utils.py b/tests/utils.py +index 8548c4f4..b9dc443c 100644 +--- a/tests/utils.py ++++ b/tests/utils.py +@@ -3,6 +3,7 @@ import decimal + import os + + from vyper import ast as vy_ast ++from vyper.compiler.phases import CompilerData + from vyper.semantics.analysis.constant_folding import constant_fold + from vyper.utils import DECIMAL_EPSILON, round_towards_zero + +@@ -28,3 +29,24 @@ def parse_and_fold(source_code): + def decimal_to_int(*args): + s = decimal.Decimal(*args) + return round_towards_zero(s / DECIMAL_EPSILON) ++ ++ ++def check_precompile_asserts(source_code): ++ # common sanity check for some tests, that calls to precompiles ++ # are correctly wrapped in an assert. ++ ++ compiler_data = CompilerData(source_code) ++ deploy_ir = compiler_data.ir_nodes ++ runtime_ir = compiler_data.ir_runtime ++ ++ def _check(ir_node, parent=None): ++ if ir_node.value == "staticcall": ++ precompile_addr = ir_node.args[1] ++ if isinstance(precompile_addr.value, int) and precompile_addr.value < 10: ++ assert parent is not None and parent.value == "assert" ++ for arg in ir_node.args: ++ _check(arg, ir_node) ++ ++ _check(deploy_ir) ++ # technically runtime_ir is contained in deploy_ir, but check it anyways. ++ _check(runtime_ir) +diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py +index 5c7e3a6a..103f147d 100644 +--- a/vyper/builtins/functions.py ++++ b/vyper/builtins/functions.py +@@ -781,7 +781,7 @@ class ECRecover(BuiltinFunctionT): + ["mstore", add_ofst(input_buf, 32), args[1]], + ["mstore", add_ofst(input_buf, 64), args[2]], + ["mstore", add_ofst(input_buf, 96), args[3]], +- ["staticcall", "gas", 1, input_buf, 128, output_buf, 32], ++ ["assert", ["staticcall", "gas", 1, input_buf, 128, output_buf, 32]], + ["mload", output_buf], + ], + typ=AddressT(), +diff --git a/vyper/codegen/core.py b/vyper/codegen/core.py +index 25a6d06f..5caa1717 100644 +--- a/vyper/codegen/core.py ++++ b/vyper/codegen/core.py +@@ -325,7 +325,7 @@ def copy_bytes(dst, src, length, length_bound): + copy_op = ["mcopy", dst, src, length] + gas_bound = _mcopy_gas_bound(length_bound) + else: +- copy_op = ["staticcall", "gas", 4, src, length, dst, length] ++ copy_op = ["assert", ["staticcall", "gas", 4, src, length, dst, length]] + gas_bound = _identity_gas_bound(length_bound) + elif src.location == CALLDATA: + copy_op = ["calldatacopy", dst, src, length] diff --git a/vyper.spec b/vyper.spec index 3e429be..2d42a3c 100644 --- a/vyper.spec +++ b/vyper.spec @@ -16,6 +16,7 @@ Patch5: vyper-0005-Relax-lark-requirement-testing-only-anyway.patch Patch6: vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch Patch7: vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch Patch8: vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch +Patch9: vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch BuildRequires: git BuildRequires: python3-cached_property BuildRequires: python3-devel From 9957d76373e8052c4799c0ac640c79001189c14e Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 2 Mar 2025 21:06:03 +0100 Subject: [PATCH 04/17] Vyper ver. 0.4.1 Signed-off-by: Peter Lemenkov --- .gitignore | 2 +- sources | 2 +- vyper-0001-Use-Cryptodomex.patch | 8 +- vyper-0002-Ease-version-requirements.patch | 11 +- ...yper-0003-Remove-unnecessary-shebang.patch | 0 ... => vyper-0004-Relax-lark-dependency.patch | 16 +- ...lark-requirement-testing-only-anyway.patch | 19 - ...g-define-rounding-mode-for-sqrt-4486.patch | 51 --- ...degen-cache-result-of-iter-eval-4488.patch | 73 ---- ...-disable-augassign-with-overlap-4487.patch | 210 --------- ...assertions-for-certain-precompiles-4.patch | 404 ------------------ vyper.spec | 14 +- 12 files changed, 21 insertions(+), 789 deletions(-) rename vyper-0004-Remove-unnecessary-shebang.patch => vyper-0003-Remove-unnecessary-shebang.patch (100%) rename vyper-0003-Lark-should-go-to-the-main-install-section-as-it-use.patch => vyper-0004-Relax-lark-dependency.patch (52%) delete mode 100644 vyper-0005-Relax-lark-requirement-testing-only-anyway.patch delete mode 100644 vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch delete mode 100644 vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch delete mode 100644 vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch delete mode 100644 vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch diff --git a/.gitignore b/.gitignore index d7a5f58..425623f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/vyper-0.4.0.tar.gz +/vyper-[0-9]*.[0-9]*.[0-9]*.tar.gz diff --git a/sources b/sources index 27f2859..bf5f162 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (vyper-0.4.0.tar.gz) = 0ad76740ba745a554ccbdc938a8f71a4df22bc9e459e645b128d1cfebcc54975e0941976874fc26196c9149c12e44bdb5d5be18ec2a9b0fecb31a2a9c530b98c +SHA512 (vyper-0.4.1.tar.gz) = 98d03ae6cd7d268b5292f52ac2b7a7b24e3250674a959afff1cc95ce17cde534416381c80e66d5a29c0b6e970557f5f6e52396e714abfd3c4bd745deb8a02cda diff --git a/vyper-0001-Use-Cryptodomex.patch b/vyper-0001-Use-Cryptodomex.patch index 01dbd09..6585c26 100644 --- a/vyper-0001-Use-Cryptodomex.patch +++ b/vyper-0001-Use-Cryptodomex.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use Cryptodomex Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index 6e48129c..e2b0688d 100644 +index e6d4c576..28ba7ebd 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ setup( @@ -15,13 +15,13 @@ index 6e48129c..e2b0688d 100644 - "pycryptodome>=3.5.1,<4", + "pycryptodomex>=3.5.1,<4", "packaging>=23.1,<24", + "lark>=1.0.0,<2", "importlib-metadata", - "wheel", diff --git a/vyper/utils.py b/vyper/utils.py -index 2b95485f..1968c281 100644 +index d1327475..9e5f5863 100644 --- a/vyper/utils.py +++ b/vyper/utils.py -@@ -184,7 +184,7 @@ decimal.setcontext(DecimalContextOverride(prec=78)) +@@ -217,7 +217,7 @@ decimal.setcontext(DecimalContextOverride(prec=78)) try: diff --git a/vyper-0002-Ease-version-requirements.patch b/vyper-0002-Ease-version-requirements.patch index 8ad275d..2cb754a 100644 --- a/vyper-0002-Ease-version-requirements.patch +++ b/vyper-0002-Ease-version-requirements.patch @@ -5,20 +5,21 @@ Subject: [PATCH] Ease version requirements Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index e2b0688d..82755981 100644 +index 28ba7ebd..2dd2d9b9 100644 --- a/setup.py +++ b/setup.py -@@ -93,11 +93,11 @@ setup( +@@ -93,12 +93,12 @@ setup( "cbor2>=5.4.6,<6", "asttokens>=2.0.5,<3", "pycryptodomex>=3.5.1,<4", - "packaging>=23.1,<24", + "packaging>=23.1", + "lark>=1.0.0,<2", "importlib-metadata", "wheel", ], -- setup_requires=["pytest-runner", "setuptools_scm>=7.1.0,<8.0.0"], -+ setup_requires=["pytest-runner", "setuptools_scm>=7.1.0"], - tests_require=extras_require["test"], +- setup_requires=["setuptools_scm>=7.1.0,<8.0.0"], ++ setup_requires=["setuptools_scm>=7.1.0"], extras_require=extras_require, entry_points={ + "console_scripts": [ diff --git a/vyper-0004-Remove-unnecessary-shebang.patch b/vyper-0003-Remove-unnecessary-shebang.patch similarity index 100% rename from vyper-0004-Remove-unnecessary-shebang.patch rename to vyper-0003-Remove-unnecessary-shebang.patch diff --git a/vyper-0003-Lark-should-go-to-the-main-install-section-as-it-use.patch b/vyper-0004-Relax-lark-dependency.patch similarity index 52% rename from vyper-0003-Lark-should-go-to-the-main-install-section-as-it-use.patch rename to vyper-0004-Relax-lark-dependency.patch index a756bfc..4607507 100644 --- a/vyper-0003-Lark-should-go-to-the-main-install-section-as-it-use.patch +++ b/vyper-0004-Relax-lark-dependency.patch @@ -1,27 +1,19 @@ From: Peter Lemenkov Date: Thu, 25 Jul 2024 23:18:23 +0200 -Subject: [PATCH] Lark should go to the main install section as it used not - only for tests +Subject: [PATCH] Relax lark dependency Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index 82755981..94477f03 100644 +index 2dd2d9b9..0a3367ca 100644 --- a/setup.py +++ b/setup.py -@@ -15,7 +15,6 @@ extras_require = { +@@ -15,7 +15,7 @@ extras_require = { "pytest-split>=0.7.0,<1.0", "eth_abi>=5.0.0,<6.0.0", "py-evm>=0.10.1b1,<0.11", - "lark==1.1.9", ++ "lark>=1.1.7", "hypothesis[lark]>=6.0,<7.0", "eth-stdlib==0.2.7", "eth-account==0.12.2", -@@ -92,6 +91,7 @@ setup( - install_requires=[ - "cbor2>=5.4.6,<6", - "asttokens>=2.0.5,<3", -+ "lark==1.1.9", - "pycryptodomex>=3.5.1,<4", - "packaging>=23.1", - "importlib-metadata", diff --git a/vyper-0005-Relax-lark-requirement-testing-only-anyway.patch b/vyper-0005-Relax-lark-requirement-testing-only-anyway.patch deleted file mode 100644 index 8ec768e..0000000 --- a/vyper-0005-Relax-lark-requirement-testing-only-anyway.patch +++ /dev/null @@ -1,19 +0,0 @@ -From: Peter Lemenkov -Date: Thu, 8 Aug 2024 20:33:53 +0200 -Subject: [PATCH] Relax lark requirement (testing only anyway) - -Signed-off-by: Peter Lemenkov - -diff --git a/setup.py b/setup.py -index 94477f03..61cf2f76 100644 ---- a/setup.py -+++ b/setup.py -@@ -91,7 +91,7 @@ setup( - install_requires=[ - "cbor2>=5.4.6,<6", - "asttokens>=2.0.5,<3", -- "lark==1.1.9", -+ "lark>=1.1.7", - "pycryptodomex>=3.5.1,<4", - "packaging>=23.1", - "importlib-metadata", diff --git a/vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch b/vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch deleted file mode 100644 index 841c453..0000000 --- a/vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch +++ /dev/null @@ -1,51 +0,0 @@ -From: Charles Cooper -Date: Sun, 23 Feb 2025 10:38:22 +0100 -Subject: [PATCH] fix[lang]: define rounding mode for sqrt (#4486) - -prior to this commit, the rounding mode for `sqrt()` is undefined, -which could be an issue for applications which use `sqrt()` to -determine boundary conditions. this commit ensures the result is -rounded down. - -diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst -index a0e424ad..6ac659bd 100644 ---- a/docs/built-in-functions.rst -+++ b/docs/built-in-functions.rst -@@ -711,7 +711,7 @@ Math - - .. py:function:: sqrt(d: decimal) -> decimal - -- Return the square root of the provided decimal number, using the Babylonian square root algorithm. -+ Return the square root of the provided decimal number, using the Babylonian square root algorithm. The rounding mode is to round down to the nearest epsilon. For instance, ``sqrt(0.9999999998) == 0.9999999998``. - - .. code-block:: vyper - -diff --git a/tests/functional/codegen/types/numbers/test_sqrt.py b/tests/functional/codegen/types/numbers/test_sqrt.py -index cf62cecd..54b762c4 100644 ---- a/tests/functional/codegen/types/numbers/test_sqrt.py -+++ b/tests/functional/codegen/types/numbers/test_sqrt.py -@@ -146,6 +146,10 @@ def test_sqrt_bounds(sqrt_contract, value): - ) - @hypothesis.example(value=Decimal(SizeLimits.MAX_INT128)) - @hypothesis.example(value=Decimal(0)) -+# cf. GHSA-2p94-8669-xg86 for the following three examples: -+@hypothesis.example(value=Decimal("0.9999999998")) -+@hypothesis.example(value=Decimal("0.9999999997")) -+@hypothesis.example(value=Decimal("1.1000000000")) - def test_sqrt_valid_range(sqrt_contract, value): - vyper_sqrt = sqrt_contract.test(decimal_to_int(value)) - actual_sqrt = decimal_sqrt(value) -diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py -index 672d9784..5c7e3a6a 100644 ---- a/vyper/builtins/functions.py -+++ b/vyper/builtins/functions.py -@@ -2142,6 +2142,9 @@ else: - break - y = z - z = (x / z + z) / 2.0 -+ -+ if y < z: -+ z = y - """ - - x_type = DecimalT() diff --git a/vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch b/vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch deleted file mode 100644 index 1bdc18b..0000000 --- a/vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch +++ /dev/null @@ -1,73 +0,0 @@ -From: Charles Cooper -Date: Sat, 22 Feb 2025 19:34:41 +0100 -Subject: [PATCH] fix[codegen]: cache result of iter eval (#4488) - -prior to this commit, multiple evaluation of a single expression is -possible in the iterator target of a for loop. while the iterator -expression cannot produce multiple writes, it can _consume_ side -effects produced in the loop body (e.g. read a storage variable -updated in the loop body) and thus lead to unexpected program -behavior. specifically, reads in iterators which contain an `IfExp` -(e.g. `for s: uint256 in ([read(), read()] if True else []))` would -issue one evaluation of the list `[read(), read()]` per loop iteration, -thus interleaving reads with writes in the loop body. - -this commit fixes the issue by using `cache_when_complex` to enforce -evaluation of the iterator before entering the loop body. - -this is incidentally also a performance fix, since it moves the -repeated evaluation into a one-time evaluation. - -references: -- https://github.com/vyperlang/vyper/security/advisories/GHSA-h33q-mhmp-8p67 - -diff --git a/vyper/codegen/stmt.py b/vyper/codegen/stmt.py -index 830f2f92..165252d5 100644 ---- a/vyper/codegen/stmt.py -+++ b/vyper/codegen/stmt.py -@@ -267,20 +267,21 @@ class Stmt: - ret.append(make_setter(tmp_list, iter_list)) - iter_list = tmp_list - -- # set up the loop variable -- e = get_element_ptr(iter_list, i, array_bounds_check=False) -- body = ["seq", make_setter(loop_var, e), parse_body(self.stmt.body, self.context)] -- -- repeat_bound = iter_list.typ.count -- if isinstance(iter_list.typ, DArrayT): -- array_len = get_dyn_array_count(iter_list) -- else: -- array_len = repeat_bound -+ with iter_list.cache_when_complex("list_iter") as (b1, iter_list): -+ # set up the loop variable -+ e = get_element_ptr(iter_list, i, array_bounds_check=False) -+ body = ["seq", make_setter(loop_var, e), parse_body(self.stmt.body, self.context)] -+ -+ repeat_bound = iter_list.typ.count -+ if isinstance(iter_list.typ, DArrayT): -+ array_len = get_dyn_array_count(iter_list) -+ else: -+ array_len = repeat_bound - -- ret.append(["repeat", i, 0, array_len, repeat_bound, body]) -+ ret.append(["repeat", i, 0, array_len, repeat_bound, body]) - -- del self.context.forvars[varname] -- return IRnode.from_list(ret) -+ del self.context.forvars[varname] -+ return b1.resolve(IRnode.from_list(ret)) - - def parse_AugAssign(self): - target = self._get_target(self.stmt.target) -diff --git a/vyper/semantics/analysis/local.py b/vyper/semantics/analysis/local.py -index 26c6a4ef..909f5b7b 100644 ---- a/vyper/semantics/analysis/local.py -+++ b/vyper/semantics/analysis/local.py -@@ -522,6 +522,7 @@ class FunctionAnalyzer(VyperNodeVisitorBase): - def _analyse_range_iter(self, iter_node, target_type): - # iteration via range() - if iter_node.get("func.id") != "range": -+ # CMC 2025-02-12 I think we can allow this actually - raise IteratorException("Cannot iterate over the result of a function call", iter_node) - _validate_range_call(iter_node) - diff --git a/vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch b/vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch deleted file mode 100644 index 6665af3..0000000 --- a/vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch +++ /dev/null @@ -1,210 +0,0 @@ -From: Charles Cooper -Date: Sun, 23 Feb 2025 20:51:52 +0100 -Subject: [PATCH] fix[codegen]: disable augassign with overlap (#4487) - -in vyper, the behavior for AugAssign is to perform the bounds checks -only before evaluation of the rhs, rather than before-and-after. in -other words, the following code: - -```vyper -def poc(): - a: DynArray[uint256, 2] = [1, 2] - a[1] += a.pop() -``` - -is equivalent to: - -```vyper -def poc(): - a: DynArray[uint256, 2] = [1, 2] - a[1] += a[len(a) - 1] - a.pop() -``` - -rather than: - -```vyper -def poc(): - a: DynArray[uint256, 2] = [1, 2] - s: uint256 = a[1] - t: uint256 = a.pop() - a[1] = s + t # reverts due to oob access -``` - -this commit blocks the potentially missing bounds check by panicking -when there is a potential write on the rhs of an AugAssign which could -change the length on the lhs. - -references: -- https://github.com/vyperlang/vyper/security/advisories/GHSA-4w26-8p97-f4jp - ---------- - -Co-authored-by: cyberthirst - -diff --git a/tests/functional/codegen/features/test_assignment.py b/tests/functional/codegen/features/test_assignment.py -index eaafb527..53d02dfb 100644 ---- a/tests/functional/codegen/features/test_assignment.py -+++ b/tests/functional/codegen/features/test_assignment.py -@@ -1,6 +1,6 @@ - import pytest - --from vyper.exceptions import ImmutableViolation, InvalidType, TypeMismatch -+from vyper.exceptions import CodegenPanic, ImmutableViolation, InvalidType, TypeMismatch - - - def test_augassign(get_contract): -@@ -39,6 +39,135 @@ def augmod(x: int128, y: int128) -> int128: - print("Passed aug-assignment test") - - -+@pytest.mark.parametrize( -+ "source", -+ [ -+ """ -+@external -+def poc(): -+ a: DynArray[uint256, 2] = [1, 2] -+ a[1] += a.pop() -+ """, -+ """ -+a: DynArray[uint256, 2] -+ -+def side_effect() -> uint256: -+ return self.a.pop() -+ -+@external -+def poc(): -+ self.a = [1, 2] -+ self.a[1] += self.side_effect() -+ """, -+ """ -+a: DynArray[uint256, 2] -+ -+def side_effect() -> uint256: -+ self.a = [1] -+ return 1 -+ -+@external -+def poc(): -+ self.a = [1, 2] -+ self.a[1] += self.side_effect() -+ """, -+ """ -+a: DynArray[uint256, 2] -+ -+interface Foo: -+ def foo() -> uint256: nonpayable -+ -+@external -+def foo() -> uint256: -+ return self.a.pop() -+ -+@external -+def poc(): -+ self.a = [1, 2] -+ # panics due to extcall -+ self.a[1] += extcall Foo(self).foo() -+ """, -+ ], -+) -+@pytest.mark.xfail(strict=True, raises=CodegenPanic) -+def test_augassign_oob(get_contract, tx_failed, source): -+ # xfail here (with panic): -+ c = get_contract(source) -+ -+ # not reached until the panic is fixed -+ with tx_failed(c): -+ c.poc() -+ -+ -+@pytest.mark.parametrize( -+ "source", -+ [ -+ """ -+a: public(DynArray[uint256, 2]) -+ -+interface Foo: -+ def foo() -> uint256: view -+ -+@external -+def foo() -> uint256: -+ return self.a[1] -+ -+@external -+def entry() -> DynArray[uint256, 2]: -+ self.a = [1, 1] -+ # panics due to staticcall -+ self.a[1] += staticcall Foo(self).foo() -+ return self.a -+ """ -+ ], -+) -+@pytest.mark.xfail(strict=True, raises=CodegenPanic) -+def test_augassign_rhs_references_lhs(get_contract, tx_failed, source): -+ # xfail here (with panic): -+ c = get_contract(source) -+ -+ assert c.entry() == [1, 2] -+ -+ -+@pytest.mark.parametrize( -+ "source", -+ [ -+ """ -+@external -+def entry() -> DynArray[uint256, 2]: -+ a: DynArray[uint256, 2] = [1, 1] -+ a[1] += a[1] -+ return a -+ """, -+ """ -+@external -+def entry() -> DynArray[uint256, 2]: -+ a: uint256 = 1 -+ a += a -+ b: DynArray[uint256, 2] = [a, a] -+ b[0] -= b[0] -+ b[0] += b[1] // 2 -+ return b -+ """, -+ """ -+a: DynArray[uint256, 2] -+ -+def read() -> uint256: -+ return self.a[1] -+ -+@external -+def entry() -> DynArray[uint256, 2]: -+ self.a = [1, 1] -+ self.a[1] += self.read() -+ return self.a -+ """, -+ ], -+) -+def test_augassign_rhs_references_lhs2(get_contract, source): -+ c = get_contract(source) -+ assert c.entry() == [1, 2] -+ -+ - @pytest.mark.parametrize( - "typ,in_val,out_val", - [ -diff --git a/vyper/codegen/stmt.py b/vyper/codegen/stmt.py -index 165252d5..24391e27 100644 ---- a/vyper/codegen/stmt.py -+++ b/vyper/codegen/stmt.py -@@ -293,6 +293,13 @@ class Stmt: - # single word load/stores are atomic. - raise TypeCheckFailure("unreachable") - -+ for var in target.referenced_variables: -+ if var.typ._is_prim_word: -+ continue -+ # oob - GHSA-4w26-8p97-f4jp -+ if var in right.variable_writes or right.contains_risky_call: -+ raise CodegenPanic("unreachable") -+ - with target.cache_when_complex("_loc") as (b, target): - left = IRnode.from_list(LOAD(target), typ=target.typ) - new_val = Expr.handle_binop(self.stmt.op, left, right, self.context) diff --git a/vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch b/vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch deleted file mode 100644 index 49afa86..0000000 --- a/vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch +++ /dev/null @@ -1,404 +0,0 @@ -From: Charles Cooper -Date: Mon, 20 Jan 2025 11:51:21 -0500 -Subject: [PATCH] fix[codegen]: fix assertions for certain precompiles (#4451) - -this commit fixes a flaw in code generation for certain -precompiles. specifically, some calls to the ecrecover (0x01) and -identity (0x04) precompiles were not checked for success. - -in 93a957947af1088addc, the assert for memory copying calls to the -identity precompile was optimized out; the reasoning being that if the -identity precompile fails due to OOG, the contract would also likely -fail with OOG. however, due to the 63/64ths rule, there are cases where -just enough gas was supplied to the current call context so that the -subcall to the precompile could fail with OOG, but the contract has -enough gas to continue execution after it shouldn't (which is undefined -behavior) and then successfully return out of the call context. - -(note that even prior to 93a957947af1088addc, some calls to the -identity precompile did not check the success flag. cf. commit -cf03d27be6a74c0c33de. the call to ecrecover was unchecked since -inception - db44cde626919ed8bebf). - -note also that since cancun, memory copies are implemented using -the `mcopy` instruction, so the bug as it pertains to the identity -precompile only affects pre-cancun compilation targets. - -this commit fixes the flaw by converting the relevant unchecked calls -to checked calls. - -it also adds tests that trigger the behavior by running the call, and -then performing the exact same call again but providing `gas_used` back -to the contract, which is the minimum amount of gas for the call to the -contract to finish execution. the specific amount of gas left at the -point of the subcall is small enough to cause the subcall to fail (and -the check around the subcall success to revert, which is what is tested -for in the new tests). in these tests, it also adds a static check -that the IR is well-formed (that all relevant calls to precompiles are -appropriately checked). - -references: -- https://github.com/vyperlang/vyper/security/advisories/GHSA-vgf2-gvx8-xwc3 - -diff --git a/tests/functional/builtins/codegen/test_ecrecover.py b/tests/functional/builtins/codegen/test_ecrecover.py -index 8db51fdd..47a22506 100644 ---- a/tests/functional/builtins/codegen/test_ecrecover.py -+++ b/tests/functional/builtins/codegen/test_ecrecover.py -@@ -1,7 +1,10 @@ -+import contextlib -+ - from eth_account import Account - from eth_account._utils.signing import to_bytes32 - --from tests.utils import ZERO_ADDRESS -+from tests.utils import ZERO_ADDRESS, check_precompile_asserts -+from vyper.compiler.settings import OptimizationLevel - - - def test_ecrecover_test(get_contract): -@@ -86,3 +89,40 @@ def test_ecrecover() -> bool: - """ - c = get_contract(code) - assert c.test_ecrecover() is True -+ -+ -+def test_ecrecover_oog_handling(env, get_contract, tx_failed, optimize, experimental_codegen): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+@external -+@view -+def do_ecrecover(hash: bytes32, v: uint256, r:uint256, s:uint256) -> address: -+ return ecrecover(hash, v, r, s) -+ """ -+ check_precompile_asserts(code) -+ -+ c = get_contract(code) -+ -+ h = b"\x35" * 32 -+ local_account = Account.from_key(b"\x46" * 32) -+ sig = local_account.signHash(h) -+ v, r, s = sig.v, sig.r, sig.s -+ -+ assert c.do_ecrecover(h, v, r, s) == local_account.address -+ -+ gas_used = env.last_result.gas_used -+ -+ if optimize == OptimizationLevel.NONE and not experimental_codegen: -+ # if optimizations are off, enough gas is used by the contract -+ # that the gas provided to ecrecover (63/64ths rule) is enough -+ # for it to succeed -+ ctx = contextlib.nullcontext -+ else: -+ # in other cases, the gas forwarded is small enough for ecrecover -+ # to fail with oog, which we handle by reverting. -+ ctx = tx_failed -+ -+ with ctx(): -+ # provide enough spare gas for the top-level call to not oog but -+ # not enough for ecrecover to succeed -+ c.do_ecrecover(h, v, r, s, gas=gas_used) -diff --git a/tests/functional/codegen/types/test_dynamic_array.py b/tests/functional/codegen/types/test_dynamic_array.py -index 2a0f4e77..b5247efb 100644 ---- a/tests/functional/codegen/types/test_dynamic_array.py -+++ b/tests/functional/codegen/types/test_dynamic_array.py -@@ -1,10 +1,12 @@ -+import contextlib - import itertools - from typing import Any, Callable - - import pytest - --from tests.utils import decimal_to_int -+from tests.utils import check_precompile_asserts, decimal_to_int - from vyper.compiler import compile_code -+from vyper.evm.opcodes import version_check - from vyper.exceptions import ( - ArgumentException, - ArrayIndexException, -@@ -1903,3 +1905,59 @@ def foo(): - c = get_contract(code) - with tx_failed(): - c.foo() -+ -+ -+def test_dynarray_copy_oog(env, get_contract, tx_failed): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+ -+@external -+def foo(a: DynArray[uint256, 4000]) -> uint256: -+ b: DynArray[uint256, 4000] = a -+ return b[0] -+ """ -+ check_precompile_asserts(code) -+ -+ c = get_contract(code) -+ dynarray = [2] * 4000 -+ assert c.foo(dynarray) == 2 -+ -+ gas_used = env.last_result.gas_used -+ if version_check(begin="cancun"): -+ ctx = contextlib.nullcontext -+ else: -+ ctx = tx_failed -+ -+ with ctx(): -+ # depends on EVM version. pre-cancun, will revert due to checking -+ # success flag from identity precompile. -+ c.foo(dynarray, gas=gas_used) -+ -+ -+def test_dynarray_copy_oog2(env, get_contract, tx_failed): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+@external -+@view -+def foo(x: String[1000000], y: String[1000000]) -> DynArray[String[1000000], 2]: -+ z: DynArray[String[1000000], 2] = [x, y] -+ # Some code -+ return z -+ """ -+ check_precompile_asserts(code) -+ -+ c = get_contract(code) -+ calldata0 = "a" * 10 -+ calldata1 = "b" * 1000000 -+ assert c.foo(calldata0, calldata1) == [calldata0, calldata1] -+ -+ gas_used = env.last_result.gas_used -+ if version_check(begin="cancun"): -+ ctx = contextlib.nullcontext -+ else: -+ ctx = tx_failed -+ -+ with ctx(): -+ # depends on EVM version. pre-cancun, will revert due to checking -+ # success flag from identity precompile. -+ c.foo(calldata0, calldata1, gas=gas_used) -diff --git a/tests/functional/codegen/types/test_lists.py b/tests/functional/codegen/types/test_lists.py -index 953a9a9f..26cd16ed 100644 ---- a/tests/functional/codegen/types/test_lists.py -+++ b/tests/functional/codegen/types/test_lists.py -@@ -1,8 +1,12 @@ -+import contextlib - import itertools - - import pytest - --from tests.utils import decimal_to_int -+from tests.evm_backends.base_env import EvmError -+from tests.utils import check_precompile_asserts, decimal_to_int -+from vyper.compiler.settings import OptimizationLevel -+from vyper.evm.opcodes import version_check - from vyper.exceptions import ArrayIndexException, OverflowException, TypeMismatch - - -@@ -848,3 +852,73 @@ def foo() -> {return_type}: - return MY_CONSTANT[0][0] - """ - assert_compile_failed(lambda: get_contract(code), TypeMismatch) -+ -+ -+def test_array_copy_oog(env, get_contract, tx_failed, optimize, experimental_codegen, request): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+@internal -+def bar(x: uint256[3000]) -> uint256[3000]: -+ a: uint256[3000] = x -+ return a -+ -+@external -+def foo(x: uint256[3000]) -> uint256: -+ s: uint256[3000] = self.bar(x) -+ return s[0] -+ """ -+ check_precompile_asserts(code) -+ -+ if optimize == OptimizationLevel.NONE and not experimental_codegen: -+ # fails in bytecode generation due to jumpdests too large -+ with pytest.raises(AssertionError): -+ get_contract(code) -+ return -+ -+ c = get_contract(code) -+ array = [2] * 3000 -+ assert c.foo(array) == array[0] -+ -+ # get the minimum gas for the contract complete execution -+ gas_used = env.last_result.gas_used -+ if version_check(begin="cancun"): -+ ctx = contextlib.nullcontext -+ else: -+ ctx = tx_failed -+ with ctx(): -+ # depends on EVM version. pre-cancun, will revert due to checking -+ # success flag from identity precompile. -+ c.foo(array, gas=gas_used) -+ -+ -+def test_array_copy_oog2(env, get_contract, tx_failed, optimize, experimental_codegen, request): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+@external -+def foo(x: uint256[2500]) -> uint256: -+ s: uint256[2500] = x -+ t: uint256[2500] = s -+ return t[0] -+ """ -+ check_precompile_asserts(code) -+ -+ if optimize == OptimizationLevel.NONE and not experimental_codegen: -+ # fails in creating contract due to code too large -+ with tx_failed(EvmError): -+ get_contract(code) -+ return -+ -+ c = get_contract(code) -+ array = [2] * 2500 -+ assert c.foo(array) == array[0] -+ -+ # get the minimum gas for the contract complete execution -+ gas_used = env.last_result.gas_used -+ if version_check(begin="cancun"): -+ ctx = contextlib.nullcontext -+ else: -+ ctx = tx_failed -+ with ctx(): -+ # depends on EVM version. pre-cancun, will revert due to checking -+ # success flag from identity precompile. -+ c.foo(array, gas=gas_used) -diff --git a/tests/functional/codegen/types/test_string.py b/tests/functional/codegen/types/test_string.py -index 51899b50..2e340e8f 100644 ---- a/tests/functional/codegen/types/test_string.py -+++ b/tests/functional/codegen/types/test_string.py -@@ -1,5 +1,10 @@ -+import contextlib -+ - import pytest - -+from tests.utils import check_precompile_asserts -+from vyper.evm.opcodes import version_check -+ - - def test_string_return(get_contract): - code = """ -@@ -359,3 +364,56 @@ def compare_var_storage_not_equal_false() -> bool: - assert c.compare_var_storage_equal_false() is False - assert c.compare_var_storage_not_equal_true() is True - assert c.compare_var_storage_not_equal_false() is False -+ -+ -+def test_string_copy_oog(env, get_contract, tx_failed): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+@external -+@view -+def foo(x: String[1000000]) -> String[1000000]: -+ return x -+ """ -+ check_precompile_asserts(code) -+ -+ c = get_contract(code) -+ calldata = "a" * 1000000 -+ assert c.foo(calldata) == calldata -+ -+ gas_used = env.last_result.gas_used -+ if version_check(begin="cancun"): -+ ctx = contextlib.nullcontext -+ else: -+ ctx = tx_failed -+ -+ with ctx(): -+ # depends on EVM version. pre-cancun, will revert due to checking -+ # success flag from identity precompile. -+ c.foo(calldata, gas=gas_used) -+ -+ -+def test_string_copy_oog2(env, get_contract, tx_failed): -+ # GHSA-vgf2-gvx8-xwc3 -+ code = """ -+@external -+@view -+def foo(x: String[1000000]) -> uint256: -+ y: String[1000000] = x -+ return len(y) -+ """ -+ check_precompile_asserts(code) -+ -+ c = get_contract(code) -+ calldata = "a" * 1000000 -+ assert c.foo(calldata) == len(calldata) -+ -+ gas_used = env.last_result.gas_used -+ if version_check(begin="cancun"): -+ ctx = contextlib.nullcontext -+ else: -+ ctx = tx_failed -+ -+ with ctx(): -+ # depends on EVM version. pre-cancun, will revert due to checking -+ # success flag from identity precompile. -+ c.foo(calldata, gas=gas_used) -diff --git a/tests/utils.py b/tests/utils.py -index 8548c4f4..b9dc443c 100644 ---- a/tests/utils.py -+++ b/tests/utils.py -@@ -3,6 +3,7 @@ import decimal - import os - - from vyper import ast as vy_ast -+from vyper.compiler.phases import CompilerData - from vyper.semantics.analysis.constant_folding import constant_fold - from vyper.utils import DECIMAL_EPSILON, round_towards_zero - -@@ -28,3 +29,24 @@ def parse_and_fold(source_code): - def decimal_to_int(*args): - s = decimal.Decimal(*args) - return round_towards_zero(s / DECIMAL_EPSILON) -+ -+ -+def check_precompile_asserts(source_code): -+ # common sanity check for some tests, that calls to precompiles -+ # are correctly wrapped in an assert. -+ -+ compiler_data = CompilerData(source_code) -+ deploy_ir = compiler_data.ir_nodes -+ runtime_ir = compiler_data.ir_runtime -+ -+ def _check(ir_node, parent=None): -+ if ir_node.value == "staticcall": -+ precompile_addr = ir_node.args[1] -+ if isinstance(precompile_addr.value, int) and precompile_addr.value < 10: -+ assert parent is not None and parent.value == "assert" -+ for arg in ir_node.args: -+ _check(arg, ir_node) -+ -+ _check(deploy_ir) -+ # technically runtime_ir is contained in deploy_ir, but check it anyways. -+ _check(runtime_ir) -diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py -index 5c7e3a6a..103f147d 100644 ---- a/vyper/builtins/functions.py -+++ b/vyper/builtins/functions.py -@@ -781,7 +781,7 @@ class ECRecover(BuiltinFunctionT): - ["mstore", add_ofst(input_buf, 32), args[1]], - ["mstore", add_ofst(input_buf, 64), args[2]], - ["mstore", add_ofst(input_buf, 96), args[3]], -- ["staticcall", "gas", 1, input_buf, 128, output_buf, 32], -+ ["assert", ["staticcall", "gas", 1, input_buf, 128, output_buf, 32]], - ["mload", output_buf], - ], - typ=AddressT(), -diff --git a/vyper/codegen/core.py b/vyper/codegen/core.py -index 25a6d06f..5caa1717 100644 ---- a/vyper/codegen/core.py -+++ b/vyper/codegen/core.py -@@ -325,7 +325,7 @@ def copy_bytes(dst, src, length, length_bound): - copy_op = ["mcopy", dst, src, length] - gas_bound = _mcopy_gas_bound(length_bound) - else: -- copy_op = ["staticcall", "gas", 4, src, length, dst, length] -+ copy_op = ["assert", ["staticcall", "gas", 4, src, length, dst, length]] - gas_bound = _identity_gas_bound(length_bound) - elif src.location == CALLDATA: - copy_op = ["calldatacopy", dst, src, length] diff --git a/vyper.spec b/vyper.spec index 2d42a3c..0f08303 100644 --- a/vyper.spec +++ b/vyper.spec @@ -1,8 +1,8 @@ -%global git_commit e9db8d9f7486eae38f5b86531629019ad28f514e +%global git_commit 8a93dd27de503b0a3ace36d961a10c9ea4adee8b Summary: Pythonic Smart Contract Language for the EVM Name: vyper -Version: 0.4.0 +Version: 0.4.1 Release: %autorelease BuildArch: noarch License: Apache-2.0 @@ -10,13 +10,8 @@ URL: https://vyperlang.org Source0: %{pypi_source %{name}} Patch1: vyper-0001-Use-Cryptodomex.patch Patch2: vyper-0002-Ease-version-requirements.patch -Patch3: vyper-0003-Lark-should-go-to-the-main-install-section-as-it-use.patch -Patch4: vyper-0004-Remove-unnecessary-shebang.patch -Patch5: vyper-0005-Relax-lark-requirement-testing-only-anyway.patch -Patch6: vyper-0006-fix-lang-define-rounding-mode-for-sqrt-4486.patch -Patch7: vyper-0007-fix-codegen-cache-result-of-iter-eval-4488.patch -Patch8: vyper-0008-fix-codegen-disable-augassign-with-overlap-4487.patch -Patch9: vyper-0009-fix-codegen-fix-assertions-for-certain-precompiles-4.patch +Patch3: vyper-0003-Remove-unnecessary-shebang.patch +Patch4: vyper-0004-Relax-lark-dependency.patch BuildRequires: git BuildRequires: python3-cached_property BuildRequires: python3-devel @@ -54,6 +49,7 @@ rm -f %{buildroot}/usr/vyper_git_commithash.txt %files -f %{pyproject_files} %doc README.md SECURITY.md %{_bindir}/fang +%{_bindir}/venom %{_bindir}/vyper %{_bindir}/vyper-json From 1d114ff4ff676a083630668fa9cdbe4b28ff35b4 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Mon, 31 Mar 2025 21:13:30 +0200 Subject: [PATCH 05/17] F41+: Use the provisional declarative buildsystem Signed-off-by: Peter Lemenkov --- vyper.spec | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/vyper.spec b/vyper.spec index 0f08303..738e340 100644 --- a/vyper.spec +++ b/vyper.spec @@ -14,34 +14,25 @@ Patch3: vyper-0003-Remove-unnecessary-shebang.patch Patch4: vyper-0004-Relax-lark-dependency.patch BuildRequires: git BuildRequires: python3-cached_property -BuildRequires: python3-devel BuildRequires: python3-eth-abi BuildRequires: python3-eth-keys BuildRequires: python3-eth-stdlib BuildRequires: python3-hypothesis BuildRequires: python3-pytest BuildRequires: python3-rlp +BuildSystem: pyproject +BuildOption(install): -l %{name} %description %{summary}. -%prep -%autosetup -p1 +%prep -a echo %{sub %git_commit 0 7} > ./vyper/vyper_git_commithash.txt -%generate_buildrequires -%pyproject_buildrequires - -%build -%pyproject_wheel - -%install -%pyproject_install -%pyproject_save_files -l %{name} +%install -a rm -f %{buildroot}/usr/vyper_git_commithash.txt -%check -%pyproject_check_import +%check -a # FIXME requires https://github.com/ethereum/py-evm # FIXME requires https://github.com/paradigmxyz/pyrevm #%%pytest From 4f32d90106f5bd8ee2403529c84fe4c5265fd6c4 Mon Sep 17 00:00:00 2001 From: Romain Geissler Date: Thu, 24 Apr 2025 23:27:01 +0000 Subject: [PATCH 06/17] Relax asttokens version dependency. --- vyper-0005-Relax-asttokens-dependency.patch | 22 +++++++++++++++++++++ vyper.spec | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 vyper-0005-Relax-asttokens-dependency.patch diff --git a/vyper-0005-Relax-asttokens-dependency.patch b/vyper-0005-Relax-asttokens-dependency.patch new file mode 100644 index 0000000..0c9909e --- /dev/null +++ b/vyper-0005-Relax-asttokens-dependency.patch @@ -0,0 +1,22 @@ +From 38682c24e45722cff98dc0ad8ce7d9dcfd5baaed Mon Sep 17 00:00:00 2001 +From: Romain Geissler +Date: Thu, 24 Apr 2025 12:52:30 +0000 +Subject: [PATCH] chore[tool]: widen version bounds for `asttokens` + +--- + setup.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/setup.py b/setup.py +index 4ff9d9d861..f61f452f7f 100644 +--- a/setup.py ++++ b/setup.py +@@ -91,7 +91,7 @@ def _global_version(version): + py_modules=["vyper"], + install_requires=[ + "cbor2>=5.4.6,<6", +- "asttokens>=2.0.5,<3", ++ "asttokens>=2.0.5,<4", + "pycryptodomex>=3.5.1,<4", + "packaging>=23.1", + "lark>=1.0.0,<2", diff --git a/vyper.spec b/vyper.spec index 738e340..85abc99 100644 --- a/vyper.spec +++ b/vyper.spec @@ -12,6 +12,8 @@ Patch1: vyper-0001-Use-Cryptodomex.patch Patch2: vyper-0002-Ease-version-requirements.patch Patch3: vyper-0003-Remove-unnecessary-shebang.patch Patch4: vyper-0004-Relax-lark-dependency.patch +# Backport of https://github.com/vyperlang/vyper/pull/4592 +Patch5: vyper-0005-Relax-asttokens-dependency.patch BuildRequires: git BuildRequires: python3-cached_property BuildRequires: python3-eth-abi From ab434e33ecd43880599e78a11bed295554250d8b Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Fri, 16 May 2025 22:22:39 +0200 Subject: [PATCH 07/17] Address two issues Signed-off-by: Peter Lemenkov --- .gitignore | 2 +- ...l-widen-version-bounds-for-asttokens.patch | 18 +++++ ...ix-removal-of-side-effects-in-concat.patch | 56 ++++++++++++++++ ...-concat-side-effect-elimination-test.patch | 37 ++++++++++ ...llow-slice-with-length-0-for-ad-hoc-.patch | 67 +++++++++++++++++++ vyper.spec | 15 +++-- 6 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch create mode 100644 vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch create mode 100644 vyper-0007-add-concat-side-effect-elimination-test.patch create mode 100644 vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch diff --git a/.gitignore b/.gitignore index 425623f..78a9770 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -/vyper-[0-9]*.[0-9]*.[0-9]*.tar.gz +/vyper-*.tar.gz diff --git a/vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch b/vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch new file mode 100644 index 0000000..4fd1fef --- /dev/null +++ b/vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch @@ -0,0 +1,18 @@ +From: Romain Geissler +Date: Thu, 24 Apr 2025 12:52:30 +0000 +Subject: [PATCH] chore[tool]: widen version bounds for `asttokens` + + +diff --git a/setup.py b/setup.py +index 0a3367ca..0098e208 100644 +--- a/setup.py ++++ b/setup.py +@@ -91,7 +91,7 @@ setup( + py_modules=["vyper"], + install_requires=[ + "cbor2>=5.4.6,<6", +- "asttokens>=2.0.5,<3", ++ "asttokens>=2.0.5,<4", + "pycryptodomex>=3.5.1,<4", + "packaging>=23.1", + "lark>=1.0.0,<2", diff --git a/vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch b/vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch new file mode 100644 index 0000000..d26ab96 --- /dev/null +++ b/vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch @@ -0,0 +1,56 @@ +From: Charles Cooper +Date: Thu, 1 May 2025 12:28:31 +0200 +Subject: [PATCH] fix[codegen]: fix removal of side effects in concat + +concat would remove side effects for zero-length arguments. fix by +removing the fastpath. + +as the test case shows, this pattern is not common in user-code. + +diff --git a/tests/functional/builtins/codegen/test_concat.py b/tests/functional/builtins/codegen/test_concat.py +index 42d11dd0..47759c0f 100644 +--- a/tests/functional/builtins/codegen/test_concat.py ++++ b/tests/functional/builtins/codegen/test_concat.py +@@ -170,6 +170,27 @@ def hoo(x: bytes32, y: bytes32) -> Bytes[64]: + print("Passed second concat tests") + + ++def test_concat_zero_length_side_effects(get_contract): ++ code = """ ++counter: public(uint256) ++ ++@external ++def test() -> Bytes[256]: ++ a: Bytes[256] = concat(b"" if self.sideeffect() else b"", b"aaaa") ++ return a ++ ++def sideeffect() -> bool: ++ self.counter += 1 ++ return True ++ """ ++ ++ c = get_contract(code) ++ ++ assert c.counter() == 0 ++ assert c.test() == b"aaaa" ++ assert c.counter() == 1 ++ ++ + def test_small_output(get_contract): + code = """ + @external +diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py +index af5045d2..94300955 100644 +--- a/vyper/builtins/functions.py ++++ b/vyper/builtins/functions.py +@@ -561,10 +561,6 @@ class Concat(BuiltinFunctionT): + dst_data = add_ofst(bytes_data_ptr(dst), ofst) + + if isinstance(arg.typ, _BytestringT): +- # Ignore empty strings +- if arg.typ.maxlen == 0: +- continue +- + with arg.cache_when_complex("arg") as (b1, arg): + argdata = bytes_data_ptr(arg) + diff --git a/vyper-0007-add-concat-side-effect-elimination-test.patch b/vyper-0007-add-concat-side-effect-elimination-test.patch new file mode 100644 index 0000000..1f09b34 --- /dev/null +++ b/vyper-0007-add-concat-side-effect-elimination-test.patch @@ -0,0 +1,37 @@ +From: cyberthirst +Date: Fri, 16 May 2025 13:42:25 +0200 +Subject: [PATCH] add concat side-effect elimination test + + +diff --git a/tests/functional/builtins/codegen/test_concat.py b/tests/functional/builtins/codegen/test_concat.py +index 47759c0f..4a55a118 100644 +--- a/tests/functional/builtins/codegen/test_concat.py ++++ b/tests/functional/builtins/codegen/test_concat.py +@@ -191,6 +191,27 @@ def sideeffect() -> bool: + assert c.counter() == 1 + + ++def test_concat_zero_length_side_effects2(get_contract): ++ code = """ ++counter: public(uint256) ++ ++@external ++def test() -> Bytes[256]: ++ a: Bytes[256] = concat(b"" if self.sideeffect() else b"", b"") ++ return a ++ ++def sideeffect() -> bool: ++ self.counter += 1 ++ return True ++ """ ++ ++ c = get_contract(code) ++ ++ assert c.counter() == 0 ++ assert c.test() == b"" ++ assert c.counter() == 1 ++ ++ + def test_small_output(get_contract): + code = """ + @external diff --git a/vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch b/vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch new file mode 100644 index 0000000..d16c0f2 --- /dev/null +++ b/vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch @@ -0,0 +1,67 @@ +From: Charles Cooper +Date: Thu, 15 May 2025 14:24:20 +0200 +Subject: [PATCH] fix[codegen]: disallow slice with length 0 for ad-hoc + locations + +previously, to increase hygiene of user code, length 0 is disallowed for +slice, except for ad-hoc locations. but for ad-hoc location, the check +was skipped. this commit adds the check for all invocations of +`slice()`, whether it is one of the ad-hoc locations or not. + +diff --git a/tests/functional/builtins/codegen/test_slice.py b/tests/functional/builtins/codegen/test_slice.py +index 3f2ce44e..61fe9838 100644 +--- a/tests/functional/builtins/codegen/test_slice.py ++++ b/tests/functional/builtins/codegen/test_slice.py +@@ -45,6 +45,25 @@ def _fail_contract(code, opt_level, exceptions): + compile_code(code, settings=settings) + + ++# tests: calldata, code, extcode ++@pytest.mark.parametrize("ad_hoc_location", ("msg.data", "self.code", "msg.sender.code")) ++def test_slice_ad_hoc_zero_length(get_contract, ad_hoc_location): ++ code = f""" ++counter: public(uint256) ++ ++@external ++def test() -> Bytes[10]: ++ b: Bytes[10]= slice({ad_hoc_location}, self.side_effect(), 0) ++ return b ++ ++def side_effect() -> uint256: ++ self.counter += 1 ++ return 0 ++ """ ++ with pytest.raises(ArgumentException): ++ compile_code(code) ++ ++ + @pytest.mark.parametrize("use_literal_start", (True, False)) + @pytest.mark.parametrize("use_literal_length", (True, False)) + @pytest.mark.parametrize("opt_level", list(OptimizationLevel)) +diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py +index 94300955..701c516d 100644 +--- a/vyper/builtins/functions.py ++++ b/vyper/builtins/functions.py +@@ -315,13 +315,17 @@ class Slice(BuiltinFunctionT): + start_literal = start_expr.value if isinstance(start_expr, vy_ast.Int) else None + length_literal = length_expr.value if isinstance(length_expr, vy_ast.Int) else None + ++ # validation ++ ++ if length_literal is not None: ++ if length_literal < 1: ++ raise ArgumentException("Length cannot be less than 1", length_expr) ++ + if not is_adhoc_slice: +- if length_literal is not None: +- if length_literal < 1: +- raise ArgumentException("Length cannot be less than 1", length_expr) ++ # arg_type.length is only valid when `not is_adhoc_slice`. + +- if length_literal > arg_type.length: +- raise ArgumentException(f"slice out of bounds for {arg_type}", length_expr) ++ if length_literal is not None and length_literal > arg_type.length: ++ raise ArgumentException(f"slice out of bounds for {arg_type}", length_expr) + + if start_literal is not None: + if start_literal > arg_type.length: diff --git a/vyper.spec b/vyper.spec index 85abc99..ecd41c9 100644 --- a/vyper.spec +++ b/vyper.spec @@ -8,12 +8,17 @@ BuildArch: noarch License: Apache-2.0 URL: https://vyperlang.org Source0: %{pypi_source %{name}} -Patch1: vyper-0001-Use-Cryptodomex.patch -Patch2: vyper-0002-Ease-version-requirements.patch -Patch3: vyper-0003-Remove-unnecessary-shebang.patch -Patch4: vyper-0004-Relax-lark-dependency.patch +Patch: vyper-0001-Use-Cryptodomex.patch +Patch: vyper-0002-Ease-version-requirements.patch +Patch: vyper-0003-Remove-unnecessary-shebang.patch +Patch: vyper-0004-Relax-lark-dependency.patch # Backport of https://github.com/vyperlang/vyper/pull/4592 -Patch5: vyper-0005-Relax-asttokens-dependency.patch +Patch: vyper-0005-Relax-asttokens-dependency.patch +# https://github.com/vyperlang/vyper/pull/4644 +Patch: vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch +Patch: vyper-0007-add-concat-side-effect-elimination-test.patch +# https://github.com/vyperlang/vyper/pull/4645 +Patch: vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch BuildRequires: git BuildRequires: python3-cached_property BuildRequires: python3-eth-abi From d5c403e3a72afdaf766ce91c7905713a0aef303f Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 1 Jun 2025 13:11:00 +0200 Subject: [PATCH 08/17] Vyper ver. 0.4.2 Signed-off-by: Peter Lemenkov --- sources | 2 +- vyper-0001-Use-Cryptodomex.patch | 22 +++--- vyper-0002-Ease-version-requirements.patch | 12 +--- vyper-0004-Relax-lark-dependency.patch | 2 +- vyper-0005-Relax-asttokens-dependency.patch | 22 ------ ...l-widen-version-bounds-for-asttokens.patch | 18 ----- ...ix-removal-of-side-effects-in-concat.patch | 56 ---------------- ...-concat-side-effect-elimination-test.patch | 37 ---------- ...llow-slice-with-length-0-for-ad-hoc-.patch | 67 ------------------- vyper.spec | 11 +-- 10 files changed, 18 insertions(+), 231 deletions(-) delete mode 100644 vyper-0005-Relax-asttokens-dependency.patch delete mode 100644 vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch delete mode 100644 vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch delete mode 100644 vyper-0007-add-concat-side-effect-elimination-test.patch delete mode 100644 vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch diff --git a/sources b/sources index bf5f162..b02d31e 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (vyper-0.4.1.tar.gz) = 98d03ae6cd7d268b5292f52ac2b7a7b24e3250674a959afff1cc95ce17cde534416381c80e66d5a29c0b6e970557f5f6e52396e714abfd3c4bd745deb8a02cda +SHA512 (vyper-0.4.2.tar.gz) = 546bd806e2636786d031c23cb06252fa688d16ae821263615676e740fc1b3fcdf722bcc2ce42a47c1df4111e57f522c3c15c1a6133884a8f860367f0edda975e diff --git a/vyper-0001-Use-Cryptodomex.patch b/vyper-0001-Use-Cryptodomex.patch index 6585c26..598b264 100644 --- a/vyper-0001-Use-Cryptodomex.patch +++ b/vyper-0001-Use-Cryptodomex.patch @@ -5,28 +5,28 @@ Subject: [PATCH] Use Cryptodomex Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index e6d4c576..28ba7ebd 100644 +index b2e3d5e7..c11a60bb 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ setup( install_requires=[ "cbor2>=5.4.6,<6", - "asttokens>=2.0.5,<3", + "asttokens>=2.0.5,<4", - "pycryptodome>=3.5.1,<4", + "pycryptodomex>=3.5.1,<4", - "packaging>=23.1,<24", + "packaging>=23.1", "lark>=1.0.0,<2", - "importlib-metadata", + "wheel", diff --git a/vyper/utils.py b/vyper/utils.py -index d1327475..9e5f5863 100644 +index 9b1084ab..2896a1e0 100644 --- a/vyper/utils.py +++ b/vyper/utils.py -@@ -217,7 +217,7 @@ decimal.setcontext(DecimalContextOverride(prec=78)) +@@ -11,7 +11,7 @@ import traceback + import warnings + from typing import Generic, Iterable, Iterator, List, Set, TypeVar, Union +-from Crypto.Hash import keccak ++from Cryptodome.Hash import keccak - try: -- from Crypto.Hash import keccak # type: ignore -+ from Cryptodome.Hash import keccak # type: ignore + from vyper.exceptions import CompilerPanic, DecimalOverrideException - keccak256 = lambda x: keccak.new(digest_bits=256, data=x).digest() # noqa: E731 - except ImportError: diff --git a/vyper-0002-Ease-version-requirements.patch b/vyper-0002-Ease-version-requirements.patch index 2cb754a..bfe3cac 100644 --- a/vyper-0002-Ease-version-requirements.patch +++ b/vyper-0002-Ease-version-requirements.patch @@ -5,18 +5,12 @@ Subject: [PATCH] Ease version requirements Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index 28ba7ebd..2dd2d9b9 100644 +index c11a60bb..fdf3794f 100644 --- a/setup.py +++ b/setup.py -@@ -93,12 +93,12 @@ setup( - "cbor2>=5.4.6,<6", - "asttokens>=2.0.5,<3", - "pycryptodomex>=3.5.1,<4", -- "packaging>=23.1,<24", -+ "packaging>=23.1", - "lark>=1.0.0,<2", - "importlib-metadata", +@@ -98,7 +98,7 @@ setup( "wheel", + "immutables", ], - setup_requires=["setuptools_scm>=7.1.0,<8.0.0"], + setup_requires=["setuptools_scm>=7.1.0"], diff --git a/vyper-0004-Relax-lark-dependency.patch b/vyper-0004-Relax-lark-dependency.patch index 4607507..936ceb1 100644 --- a/vyper-0004-Relax-lark-dependency.patch +++ b/vyper-0004-Relax-lark-dependency.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Relax lark dependency Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index 2dd2d9b9..0a3367ca 100644 +index fdf3794f..1864ae66 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ extras_require = { diff --git a/vyper-0005-Relax-asttokens-dependency.patch b/vyper-0005-Relax-asttokens-dependency.patch deleted file mode 100644 index 0c9909e..0000000 --- a/vyper-0005-Relax-asttokens-dependency.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 38682c24e45722cff98dc0ad8ce7d9dcfd5baaed Mon Sep 17 00:00:00 2001 -From: Romain Geissler -Date: Thu, 24 Apr 2025 12:52:30 +0000 -Subject: [PATCH] chore[tool]: widen version bounds for `asttokens` - ---- - setup.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/setup.py b/setup.py -index 4ff9d9d861..f61f452f7f 100644 ---- a/setup.py -+++ b/setup.py -@@ -91,7 +91,7 @@ def _global_version(version): - py_modules=["vyper"], - install_requires=[ - "cbor2>=5.4.6,<6", -- "asttokens>=2.0.5,<3", -+ "asttokens>=2.0.5,<4", - "pycryptodomex>=3.5.1,<4", - "packaging>=23.1", - "lark>=1.0.0,<2", diff --git a/vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch b/vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch deleted file mode 100644 index 4fd1fef..0000000 --- a/vyper-0005-chore-tool-widen-version-bounds-for-asttokens.patch +++ /dev/null @@ -1,18 +0,0 @@ -From: Romain Geissler -Date: Thu, 24 Apr 2025 12:52:30 +0000 -Subject: [PATCH] chore[tool]: widen version bounds for `asttokens` - - -diff --git a/setup.py b/setup.py -index 0a3367ca..0098e208 100644 ---- a/setup.py -+++ b/setup.py -@@ -91,7 +91,7 @@ setup( - py_modules=["vyper"], - install_requires=[ - "cbor2>=5.4.6,<6", -- "asttokens>=2.0.5,<3", -+ "asttokens>=2.0.5,<4", - "pycryptodomex>=3.5.1,<4", - "packaging>=23.1", - "lark>=1.0.0,<2", diff --git a/vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch b/vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch deleted file mode 100644 index d26ab96..0000000 --- a/vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch +++ /dev/null @@ -1,56 +0,0 @@ -From: Charles Cooper -Date: Thu, 1 May 2025 12:28:31 +0200 -Subject: [PATCH] fix[codegen]: fix removal of side effects in concat - -concat would remove side effects for zero-length arguments. fix by -removing the fastpath. - -as the test case shows, this pattern is not common in user-code. - -diff --git a/tests/functional/builtins/codegen/test_concat.py b/tests/functional/builtins/codegen/test_concat.py -index 42d11dd0..47759c0f 100644 ---- a/tests/functional/builtins/codegen/test_concat.py -+++ b/tests/functional/builtins/codegen/test_concat.py -@@ -170,6 +170,27 @@ def hoo(x: bytes32, y: bytes32) -> Bytes[64]: - print("Passed second concat tests") - - -+def test_concat_zero_length_side_effects(get_contract): -+ code = """ -+counter: public(uint256) -+ -+@external -+def test() -> Bytes[256]: -+ a: Bytes[256] = concat(b"" if self.sideeffect() else b"", b"aaaa") -+ return a -+ -+def sideeffect() -> bool: -+ self.counter += 1 -+ return True -+ """ -+ -+ c = get_contract(code) -+ -+ assert c.counter() == 0 -+ assert c.test() == b"aaaa" -+ assert c.counter() == 1 -+ -+ - def test_small_output(get_contract): - code = """ - @external -diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py -index af5045d2..94300955 100644 ---- a/vyper/builtins/functions.py -+++ b/vyper/builtins/functions.py -@@ -561,10 +561,6 @@ class Concat(BuiltinFunctionT): - dst_data = add_ofst(bytes_data_ptr(dst), ofst) - - if isinstance(arg.typ, _BytestringT): -- # Ignore empty strings -- if arg.typ.maxlen == 0: -- continue -- - with arg.cache_when_complex("arg") as (b1, arg): - argdata = bytes_data_ptr(arg) - diff --git a/vyper-0007-add-concat-side-effect-elimination-test.patch b/vyper-0007-add-concat-side-effect-elimination-test.patch deleted file mode 100644 index 1f09b34..0000000 --- a/vyper-0007-add-concat-side-effect-elimination-test.patch +++ /dev/null @@ -1,37 +0,0 @@ -From: cyberthirst -Date: Fri, 16 May 2025 13:42:25 +0200 -Subject: [PATCH] add concat side-effect elimination test - - -diff --git a/tests/functional/builtins/codegen/test_concat.py b/tests/functional/builtins/codegen/test_concat.py -index 47759c0f..4a55a118 100644 ---- a/tests/functional/builtins/codegen/test_concat.py -+++ b/tests/functional/builtins/codegen/test_concat.py -@@ -191,6 +191,27 @@ def sideeffect() -> bool: - assert c.counter() == 1 - - -+def test_concat_zero_length_side_effects2(get_contract): -+ code = """ -+counter: public(uint256) -+ -+@external -+def test() -> Bytes[256]: -+ a: Bytes[256] = concat(b"" if self.sideeffect() else b"", b"") -+ return a -+ -+def sideeffect() -> bool: -+ self.counter += 1 -+ return True -+ """ -+ -+ c = get_contract(code) -+ -+ assert c.counter() == 0 -+ assert c.test() == b"" -+ assert c.counter() == 1 -+ -+ - def test_small_output(get_contract): - code = """ - @external diff --git a/vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch b/vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch deleted file mode 100644 index d16c0f2..0000000 --- a/vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch +++ /dev/null @@ -1,67 +0,0 @@ -From: Charles Cooper -Date: Thu, 15 May 2025 14:24:20 +0200 -Subject: [PATCH] fix[codegen]: disallow slice with length 0 for ad-hoc - locations - -previously, to increase hygiene of user code, length 0 is disallowed for -slice, except for ad-hoc locations. but for ad-hoc location, the check -was skipped. this commit adds the check for all invocations of -`slice()`, whether it is one of the ad-hoc locations or not. - -diff --git a/tests/functional/builtins/codegen/test_slice.py b/tests/functional/builtins/codegen/test_slice.py -index 3f2ce44e..61fe9838 100644 ---- a/tests/functional/builtins/codegen/test_slice.py -+++ b/tests/functional/builtins/codegen/test_slice.py -@@ -45,6 +45,25 @@ def _fail_contract(code, opt_level, exceptions): - compile_code(code, settings=settings) - - -+# tests: calldata, code, extcode -+@pytest.mark.parametrize("ad_hoc_location", ("msg.data", "self.code", "msg.sender.code")) -+def test_slice_ad_hoc_zero_length(get_contract, ad_hoc_location): -+ code = f""" -+counter: public(uint256) -+ -+@external -+def test() -> Bytes[10]: -+ b: Bytes[10]= slice({ad_hoc_location}, self.side_effect(), 0) -+ return b -+ -+def side_effect() -> uint256: -+ self.counter += 1 -+ return 0 -+ """ -+ with pytest.raises(ArgumentException): -+ compile_code(code) -+ -+ - @pytest.mark.parametrize("use_literal_start", (True, False)) - @pytest.mark.parametrize("use_literal_length", (True, False)) - @pytest.mark.parametrize("opt_level", list(OptimizationLevel)) -diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py -index 94300955..701c516d 100644 ---- a/vyper/builtins/functions.py -+++ b/vyper/builtins/functions.py -@@ -315,13 +315,17 @@ class Slice(BuiltinFunctionT): - start_literal = start_expr.value if isinstance(start_expr, vy_ast.Int) else None - length_literal = length_expr.value if isinstance(length_expr, vy_ast.Int) else None - -+ # validation -+ -+ if length_literal is not None: -+ if length_literal < 1: -+ raise ArgumentException("Length cannot be less than 1", length_expr) -+ - if not is_adhoc_slice: -- if length_literal is not None: -- if length_literal < 1: -- raise ArgumentException("Length cannot be less than 1", length_expr) -+ # arg_type.length is only valid when `not is_adhoc_slice`. - -- if length_literal > arg_type.length: -- raise ArgumentException(f"slice out of bounds for {arg_type}", length_expr) -+ if length_literal is not None and length_literal > arg_type.length: -+ raise ArgumentException(f"slice out of bounds for {arg_type}", length_expr) - - if start_literal is not None: - if start_literal > arg_type.length: diff --git a/vyper.spec b/vyper.spec index ecd41c9..69ded75 100644 --- a/vyper.spec +++ b/vyper.spec @@ -1,8 +1,8 @@ -%global git_commit 8a93dd27de503b0a3ace36d961a10c9ea4adee8b +%global git_commit c216787f5e355478733a05fa5f0fce93fa9a7126 Summary: Pythonic Smart Contract Language for the EVM Name: vyper -Version: 0.4.1 +Version: 0.4.2 Release: %autorelease BuildArch: noarch License: Apache-2.0 @@ -12,13 +12,6 @@ Patch: vyper-0001-Use-Cryptodomex.patch Patch: vyper-0002-Ease-version-requirements.patch Patch: vyper-0003-Remove-unnecessary-shebang.patch Patch: vyper-0004-Relax-lark-dependency.patch -# Backport of https://github.com/vyperlang/vyper/pull/4592 -Patch: vyper-0005-Relax-asttokens-dependency.patch -# https://github.com/vyperlang/vyper/pull/4644 -Patch: vyper-0006-fix-codegen-fix-removal-of-side-effects-in-concat.patch -Patch: vyper-0007-add-concat-side-effect-elimination-test.patch -# https://github.com/vyperlang/vyper/pull/4645 -Patch: vyper-0008-fix-codegen-disallow-slice-with-length-0-for-ad-hoc-.patch BuildRequires: git BuildRequires: python3-cached_property BuildRequires: python3-eth-abi From ed5e24cfa07ca15ae181fae1118bedf703eece7d Mon Sep 17 00:00:00 2001 From: Python Maint Date: Wed, 16 Jul 2025 08:01:37 +0200 Subject: [PATCH 09/17] Rebuilt for Python 3.14 From 470fd8134d70ee9e45dc2d3488b3aaf3f53d8f0b Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Thu, 19 Jun 2025 10:53:04 +0200 Subject: [PATCH 10/17] Vyper ver. 0.4.3 Signed-off-by: Peter Lemenkov --- sources | 2 +- vyper-0001-Use-Cryptodomex.patch | 2 +- vyper-0002-Ease-version-requirements.patch | 2 +- vyper-0004-Relax-lark-dependency.patch | 19 ------------------- vyper.spec | 5 ++--- 5 files changed, 5 insertions(+), 25 deletions(-) delete mode 100644 vyper-0004-Relax-lark-dependency.patch diff --git a/sources b/sources index b02d31e..7ebc4bb 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -SHA512 (vyper-0.4.2.tar.gz) = 546bd806e2636786d031c23cb06252fa688d16ae821263615676e740fc1b3fcdf722bcc2ce42a47c1df4111e57f522c3c15c1a6133884a8f860367f0edda975e +SHA512 (vyper-0.4.3.tar.gz) = cda058f58deed9ed5e64179c6ccb880cefa103c93008659e09c699f0d40825eaffa1b5c19ff7f93ef80b926d6c69a43c9dbfb80113e59d6a8a457f8bbbc6fbb4 diff --git a/vyper-0001-Use-Cryptodomex.patch b/vyper-0001-Use-Cryptodomex.patch index 598b264..2f9d213 100644 --- a/vyper-0001-Use-Cryptodomex.patch +++ b/vyper-0001-Use-Cryptodomex.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Use Cryptodomex Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index b2e3d5e7..c11a60bb 100644 +index a3cb94c5..4daf01be 100644 --- a/setup.py +++ b/setup.py @@ -92,7 +92,7 @@ setup( diff --git a/vyper-0002-Ease-version-requirements.patch b/vyper-0002-Ease-version-requirements.patch index bfe3cac..f7306bf 100644 --- a/vyper-0002-Ease-version-requirements.patch +++ b/vyper-0002-Ease-version-requirements.patch @@ -5,7 +5,7 @@ Subject: [PATCH] Ease version requirements Signed-off-by: Peter Lemenkov diff --git a/setup.py b/setup.py -index c11a60bb..fdf3794f 100644 +index 4daf01be..e6613671 100644 --- a/setup.py +++ b/setup.py @@ -98,7 +98,7 @@ setup( diff --git a/vyper-0004-Relax-lark-dependency.patch b/vyper-0004-Relax-lark-dependency.patch deleted file mode 100644 index 936ceb1..0000000 --- a/vyper-0004-Relax-lark-dependency.patch +++ /dev/null @@ -1,19 +0,0 @@ -From: Peter Lemenkov -Date: Thu, 25 Jul 2024 23:18:23 +0200 -Subject: [PATCH] Relax lark dependency - -Signed-off-by: Peter Lemenkov - -diff --git a/setup.py b/setup.py -index fdf3794f..1864ae66 100644 ---- a/setup.py -+++ b/setup.py -@@ -15,7 +15,7 @@ extras_require = { - "pytest-split>=0.7.0,<1.0", - "eth_abi>=5.0.0,<6.0.0", - "py-evm>=0.10.1b1,<0.11", -- "lark==1.1.9", -+ "lark>=1.1.7", - "hypothesis[lark]>=6.0,<7.0", - "eth-stdlib==0.2.7", - "eth-account==0.12.2", diff --git a/vyper.spec b/vyper.spec index 69ded75..50eea2b 100644 --- a/vyper.spec +++ b/vyper.spec @@ -1,8 +1,8 @@ -%global git_commit c216787f5e355478733a05fa5f0fce93fa9a7126 +%global git_commit bff19ea204059290da652854cd634abef10f6c43 Summary: Pythonic Smart Contract Language for the EVM Name: vyper -Version: 0.4.2 +Version: 0.4.3 Release: %autorelease BuildArch: noarch License: Apache-2.0 @@ -11,7 +11,6 @@ Source0: %{pypi_source %{name}} Patch: vyper-0001-Use-Cryptodomex.patch Patch: vyper-0002-Ease-version-requirements.patch Patch: vyper-0003-Remove-unnecessary-shebang.patch -Patch: vyper-0004-Relax-lark-dependency.patch BuildRequires: git BuildRequires: python3-cached_property BuildRequires: python3-eth-abi From 56998c506b0c55506a3baf2b67a4eccada0ca945 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 25 Jul 2025 20:15:54 +0000 Subject: [PATCH 11/17] Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild From 9c4b862581eba9990d30aac708c59ab08f5fefff Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Sun, 10 Aug 2025 21:48:53 +0200 Subject: [PATCH 12/17] Cosmetic Signed-off-by: Peter Lemenkov --- vyper.spec | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vyper.spec b/vyper.spec index 50eea2b..7235dd1 100644 --- a/vyper.spec +++ b/vyper.spec @@ -8,7 +8,9 @@ BuildArch: noarch License: Apache-2.0 URL: https://vyperlang.org Source0: %{pypi_source %{name}} +# Fedora-specific Patch: vyper-0001-Use-Cryptodomex.patch +# Reverts https://github.com/vyperlang/vyper/pull/3613 Patch: vyper-0002-Ease-version-requirements.patch Patch: vyper-0003-Remove-unnecessary-shebang.patch BuildRequires: git From 7da69aad8569ac5be8309910ae9a5fa7be1afdf3 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 15 Aug 2025 15:22:31 +0200 Subject: [PATCH 13/17] Rebuilt for Python 3.14.0rc2 bytecode From 2212acf422a8dc42f76ce48dac3a0b09e58ac5a0 Mon Sep 17 00:00:00 2001 From: Python Maint Date: Fri, 19 Sep 2025 15:03:01 +0200 Subject: [PATCH 14/17] Rebuilt for Python 3.14.0rc3 bytecode From edd142f7e28af31c1732ef7b83e40b734d462702 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Sat, 17 Jan 2026 20:05:44 +0000 Subject: [PATCH 15/17] Rebuilt for https://fedoraproject.org/wiki/Fedora_44_Mass_Rebuild From 984fe2edce69968b8fc44f9bcd5e46d9789cfe5f Mon Sep 17 00:00:00 2001 From: Python Maint Date: Sat, 6 Jun 2026 14:46:47 +0200 Subject: [PATCH 16/17] Rebuilt for Python 3.15 From 616272143433d91a34e24eb07e90db89381f0937 Mon Sep 17 00:00:00 2001 From: Fedora Release Engineering Date: Fri, 17 Jul 2026 08:40:00 +0000 Subject: [PATCH 17/17] Rebuilt for https://fedoraproject.org/wiki/Fedora_45_Mass_Rebuild