Skip to content

Commit

Permalink
Merge branch 'master' into fix/builtins-tstorage-codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper committed Mar 27, 2024
2 parents b5af4d0 + 4595938 commit 29f11e2
Show file tree
Hide file tree
Showing 22 changed files with 238 additions and 80 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# jobs to validate pull request well-formedness

name: Validate PR metadata

on:
pull_request:
types:
- opened
- edited
- synchronize

permissions:
pull-requests: read

jobs:
validate-pr:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
name: Run conventional commit checker
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# https://github.com/amannn/action-semantic-pull-request?tab=readme-ov-file#configuration
with:
types: |
feat
fix
chore
refactor
# ci: continuous integration
# docs: documentation
# test: test suite
# lang: language changes
# ux: language changes (UX)
# tool: integration
# ir: (old) IR/codegen changes
# codegen: lowering from vyper AST to codegen
# venom: venom changes
scopes: |
ci
build
docs
test
lang
ux
tool
ir
codegen
venom
requireScope: true
subjectPattern: '^(?![A-Z]).+$'
subjectPatternError: |
Starts with uppercase letter: '{subject}'
(Full PR title: '{title}')
# type[scope]<optional !>: subject
# use [] instead of () for aesthetics
headerPattern: '^(\w*)(?:\[([\w$.\-*/ ]*)\])?!?: (.*)$'
validateSingleCommit: true
validateSingleCommitMatchesPrTitle: true
14 changes: 9 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,14 @@ jobs:
- name: Run tests
run: |
pytest \
--dist worksteal \
-m "not fuzzing" \
--optimize ${{ matrix.opt-mode }} \
--evm-version ${{ matrix.evm-version }} \
${{ matrix.debug && '--enable-compiler-debug-mode' || '' }} \
${{ matrix.memorymock && '--memorymock' || '' }} \
--showlocals -r aR \
--cov-branch \
--cov-report xml:coverage.xml \
--cov=vyper \
tests/
- name: Upload Coverage
Expand Down Expand Up @@ -192,7 +193,8 @@ jobs:
# NOTE: if the tests get poorly distributed, run this and commit the resulting `.test_durations` file to the `vyper-test-durations` repo.
# `pytest -m "fuzzing" --store-durations -r aR tests/`
- name: Fetch test-durations
run: curl --location "https://raw.githubusercontent.com/vyperlang/vyper-test-durations/master/test_durations" -o .test_durations
run: |
curl --location "https://raw.githubusercontent.com/vyperlang/vyper-test-durations/master/test_durations" -o .test_durations
- name: Run tests
run: |
Expand All @@ -201,8 +203,10 @@ jobs:
--splits 120 \
--group ${{ matrix.group }} \
--splitting-algorithm least_duration \
--dist worksteal \
-r aR tests/
--cov-branch \
--cov-report xml:coverage.xml \
--cov=vyper \
tests/
- name: Upload Coverage
uses: codecov/codecov-action@v4
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,12 @@ test:
lint: mypy black flake8 isort

mypy:
mypy --install-types --non-interactive --follow-imports=silent --ignore-missing-imports --implicit-optional -p vyper
mypy \
--disable-error-code "annotation-unchecked" \
--follow-imports=silent \
--ignore-missing-imports \
--implicit-optional \
-p vyper

black:
black -C -t py311 vyper/ tests/ setup.py --force-exclude=vyper/version.py
Expand Down
4 changes: 2 additions & 2 deletions docs/constants-and-vars.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Name Type Value
==================== ================ =========================================================
``block.coinbase`` ``address`` Current block miner's address
``block.difficulty`` ``uint256`` Current block difficulty
``block.prevrandao`` ``uint256`` Current randomness beacon provided by the beacon chain
``block.prevrandao`` ``bytes32`` Current randomness beacon provided by the beacon chain
``block.number`` ``uint256`` Current block number
``block.prevhash`` ``bytes32`` Equivalent to ``blockhash(block.number - 1)``
``block.timestamp`` ``uint256`` Current block epoch timestamp
Expand All @@ -31,7 +31,7 @@ Name Type Value

.. note::

``block.prevrandao`` is an alias for ``block.difficulty``. Since ``block.difficulty`` is considered deprecated according to `EIP-4399 <https://eips.ethereum.org/EIPS/eip-4399>`_ after "The Merge" (Paris hard fork), we recommend using ``block.prevrandao``.
``block.prevrandao`` is an alias for the ``block.difficulty`` opcode. Since ``block.difficulty`` is considered deprecated according to `EIP-4399 <https://eips.ethereum.org/EIPS/eip-4399>`_ after "The Merge" (Paris hard fork), we recommend using ``block.prevrandao``.

.. note::

Expand Down
4 changes: 2 additions & 2 deletions quicktest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
# ./quicktest.sh
# ./quicktest.sh tests/.../mytest.py

# run pytest but bail out on first error and suppress coverage.
# run pytest but bail out on first error
# useful for dev workflow
pytest --dist worksteal -q --no-cov -s --instafail -x --disable-warnings "$@"
pytest -q -s --instafail -x --disable-warnings "$@"
10 changes: 2 additions & 8 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,9 @@ line_length = 100

[tool:pytest]
addopts = -n auto
--cov-branch
--cov-report term
--cov-report html
--cov-report xml
--cov=vyper
--dist worksteal
python_files = test_*.py
testpaths = tests
xfail_strict = true
markers =
fuzzing: Run Hypothesis fuzz test suite (deselect with '-m "not fuzzing"')

[tool:mypy]
ignore_missing_imports = True
7 changes: 2 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"pytest>=8.0,<9.0",
"pytest-cov>=4.1,<5.0",
"pytest-instafail>=0.4,<1.0",
"pytest-xdist>=3.5,<4.0",
"pytest-xdist>=3.0,<3.4",
"pytest-split>=0.7.0,<1.0",
"eth-tester[py-evm]>=0.10.0b4,<0.11",
"eth_abi>=4.0.0,<5.0.0",
Expand All @@ -31,13 +31,10 @@
"isort==5.13.2",
"mypy==1.5",
],
"docs": ["recommonmark", "sphinx>=6.0,<7.0", "sphinx_rtd_theme>=1.2,<1.3"],
"dev": ["ipython", "pre-commit", "pyinstaller", "twine"],
}

extras_require["dev"] = (
extras_require["test"] + extras_require["lint"] + extras_require["docs"] + extras_require["dev"]
)
extras_require["dev"] = extras_require["dev"] + extras_require["test"] + extras_require["lint"]

with open("README.md", "r") as f:
long_description = f.read()
Expand Down
63 changes: 63 additions & 0 deletions tests/functional/builtins/codegen/test_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,66 @@ def test_slice_bytes32_calldata_extended(get_contract, code, result):
c.bar(3, "0x0001020304050607080910111213141516171819202122232425262728293031", 5).hex()
== result
)


# test cases crafted based on advisory GHSA-9x7f-gwxq-6f2c
oob_fail_list = [
"""
d: public(Bytes[256])
@external
def do_slice():
x : uint256 = max_value(uint256)
self.d = b"\x01\x02\x03\x04\x05\x06"
assert len(slice(self.d, 1, x)) == max_value(uint256)
""",
"""
@external
def do_slice():
x: uint256 = max_value(uint256)
# y == 0x3232323232323232323232323232323232323232323232323232323232323232
y: uint256 = 22704331223003175573249212746801550559464702875615796870481879217237868556850
z: uint96 = 1
if True:
placeholder : uint256[16] = [y, y, y, y, y, y, y, y, y, y, y, y, y, y, y, y]
s: String[32] = slice(uint2str(z), 1, x)
assert slice(s, 1, 2) == "22"
""",
"""
x: public(Bytes[64])
secret: uint256
@deploy
def __init__():
self.x = empty(Bytes[64])
self.secret = 42
@external
def do_slice() -> Bytes[64]:
start: uint256 = max_value(uint256) - 63
return slice(self.x, start, 64)
""",
# tests bounds check in adhoc location calldata
"""
interface IFace:
def choose_value(_x: uint256, _y: uint256, _z: uint256, idx: uint256) -> Bytes[32]: nonpayable
@external
def choose_value(_x: uint256, _y: uint256, _z: uint256, idx: uint256) -> Bytes[32]:
assert idx % 32 == 4
return slice(msg.data, idx, 32)
@external
def do_slice():
idx: uint256 = max_value(uint256) - 27
ret: uint256 = _abi_decode(extcall IFace(self).choose_value(1, 2, 3, idx), uint256)
assert ret == 0
""",
]


@pytest.mark.parametrize("bad_code", oob_fail_list)
def test_slice_buffer_oob_reverts(bad_code, get_contract, tx_failed):
c = get_contract(bad_code)
with tx_failed():
c.do_slice()
22 changes: 22 additions & 0 deletions tests/functional/codegen/features/test_assert.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def foo():

def test_assert_reason(w3, get_contract_with_gas_estimation, tx_failed, memory_mocker):
code = """
err: String[32]
@external
def test(a: int128) -> int128:
assert a > 1, "larger than one please"
Expand All @@ -43,6 +45,17 @@ def test2(a: int128, b: int128, extra_reason: String[32]) -> int128:
@external
def test3(reason_str: String[32]):
raise reason_str
@external
def test4(a: int128, reason_str: String[32]) -> int128:
self.err = reason_str
assert a > 1, self.err
return 1 + a
@external
def test5(reason_str: String[32]):
self.err = reason_str
raise self.err
"""
c = get_contract_with_gas_estimation(code)

Expand All @@ -66,6 +79,15 @@ def test3(reason_str: String[32]):
c.test3("An exception")
assert _fixup_err_str(e_info.value.args[0]) == "An exception"

assert c.test4(2, "msg") == 3
with pytest.raises(TransactionFailed) as e_info:
c.test4(0, "larger than one again please")
assert _fixup_err_str(e_info.value.args[0]) == "larger than one again please"

with pytest.raises(TransactionFailed) as e_info:
c.test5("A storage exception")
assert _fixup_err_str(e_info.value.args[0]) == "A storage exception"


invalid_code = [
"""
Expand Down
7 changes: 1 addition & 6 deletions tests/functional/codegen/modules/test_exports.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import pytest


def test_simple_export(make_input_bundle, get_contract):
lib1 = """
@external
Expand Down Expand Up @@ -131,9 +128,7 @@ def foo() -> uint256:
assert c.foo() == 5


# not sure if this one should work
@pytest.mark.xfail(reason="ambiguous spec")
def test_recursive_export(make_input_bundle, get_contract):
def test_transitive_export(make_input_bundle, get_contract):
lib1 = """
@external
def foo() -> uint256:
Expand Down
12 changes: 12 additions & 0 deletions tests/functional/codegen/types/numbers/test_exponents.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@
from hypothesis import strategies as st

from vyper.codegen.arithmetic import calculate_largest_base, calculate_largest_power
from vyper.compiler import compile_code
from vyper.exceptions import InvalidLiteral


def test_compiler_hang():
code = """
@external
def f0():
lv0: uint256 = max_value(int128) ** max_value(int128)
"""
with pytest.raises(InvalidLiteral):
compile_code(code)


@pytest.mark.fuzzing
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/grammar/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def fix_terminal(terminal: str) -> bool:
return terminal


ALLOWED_CHARS = st.characters(codec="utf-8", min_codepoint=1)
ALLOWED_CHARS = st.characters(codec="ascii", min_codepoint=1)


# With help from hyposmith
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/syntax/test_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def foo():
"""
@external
def foo():
x: uint256 = block.prevrandao + 185
x: bytes32 = block.prevrandao
if tx.origin == self:
y: Bytes[35] = concat(block.prevhash, b"dog")
""",
Expand Down
10 changes: 10 additions & 0 deletions vyper/ast/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import copy
import decimal
import functools
import math
import operator
import pickle
import sys
Expand Down Expand Up @@ -1080,6 +1081,15 @@ def _op(self, left, right):
raise TypeMismatch("Cannot perform exponentiation on decimal values.", self._parent)
if right < 0:
raise InvalidOperation("Cannot calculate a negative power", self._parent)
# prevent a compiler hang. we are ok with false positives at this
# stage since we are just trying to filter out inputs which can cause
# the compiler to hang. the others will get caught during constant
# folding or codegen.
# l**r > 2**256
# r * ln(l) > ln(2 ** 256)
# r > ln(2 ** 256) / ln(l)
if right > math.log(decimal.Decimal(2**257)) / math.log(decimal.Decimal(left)):
raise InvalidLiteral("Out of bounds", self)
return int(left**right)


Expand Down
2 changes: 1 addition & 1 deletion vyper/ast/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def parse_to_ast_with_settings(
py_ast = python_ast.parse(python_source)
except SyntaxError as e:
# TODO: Ensure 1-to-1 match of source_code:reformatted_code SyntaxErrors
raise SyntaxException(str(e), vyper_source, e.lineno, e.offset) from e
raise SyntaxException(str(e), vyper_source, e.lineno, e.offset) from None

# Add dummy function node to ensure local variables are treated as `AnnAssign`
# instead of state variables (`VariableDecl`)
Expand Down
Loading

0 comments on commit 29f11e2

Please sign in to comment.