Skip to content

Commit

Permalink
Merge branch 'master' into feat/venom/calloca
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-cooper committed Feb 13, 2025
2 parents 319ddd4 + c75a2da commit da79364
Show file tree
Hide file tree
Showing 31 changed files with 1,909 additions and 160 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-latest]
os: [ubuntu-latest, macos-latest]

steps:
- uses: actions/checkout@v4
Expand Down
133 changes: 120 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,45 @@ jobs:
- name: Run docs
run: sphinx-build -E -b html docs dist/docs -n -q --color

# Symbolic tests
symbolic-tests:
runs-on: "ubuntu-latest"
name: symbolic-tests

steps:
- uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install dependencies
run: pip install .[test]

- name: Install hevm
run: |
# hevm dependency
sudo apt-get install z3
wget --no-verbose -O hevm https://github.com/ethereum/hevm/releases/download/release/0.54.2/hevm-x86_64-linux
chmod +x hevm
mkdir -p "$HOME/.local/bin/"
mv hevm "$HOME/.local/bin/"
echo "$HOME/.local/bin/" >> $GITHUB_PATH
hevm version
- name: Run tests
run: >
pytest
-m "hevm"
--hevm
--cov-config=setup.cfg
--cov=vyper
tests/
# "Regular"/core tests.
tests:
runs-on: ${{ matrix.os || 'ubuntu' }}-latest
Expand Down Expand Up @@ -149,23 +188,24 @@ jobs:
--evm-backend ${{ matrix.evm-backend || 'revm' }}
${{ matrix.debug && '--enable-compiler-debug-mode' || '' }}
${{ matrix.experimental-codegen && '--experimental-codegen' || '' }}
--cov-branch
--cov-report xml:coverage.xml
--cov-config=setup.cfg
--cov=vyper
tests/
- name: Upload Coverage
uses: codecov/codecov-action@v5
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
name: coverage-files-${{ github.job }}-${{ strategy.job-index }}
include-hidden-files: true
path: .coverage
if-no-files-found: error

core-tests-success:
if: always()
# summary result from test matrix.
# see https://github.community/t/status-check-for-a-matrix-jobs/127354/7
runs-on: ubuntu-latest
needs: [tests]
needs: [tests, symbolic-tests]
steps:
- name: Check tests tests all succeeded
if: ${{ needs.tests.result != 'success' }}
Expand Down Expand Up @@ -209,16 +249,17 @@ jobs:
--splits 120 \
--group ${{ matrix.group }} \
--splitting-algorithm least_duration \
--cov-branch \
--cov-report xml:coverage.xml \
--cov-config=setup.cfg \
--cov=vyper \
tests/
- name: Upload Coverage
uses: codecov/codecov-action@v5
- name: Upload coverage artifact
uses: actions/upload-artifact@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.xml
name: coverage-files-${{ github.job }}-${{ strategy.job-index }}
include-hidden-files: true
path: .coverage
if-no-files-found: error

slow-tests-success:
if: always()
Expand All @@ -231,3 +272,69 @@ jobs:
- name: Check slow tests all succeeded
if: ${{ needs.fuzzing.result != 'success' }}
run: exit 1

coverage-report:
# Consolidate code coverage using `coverage combine` and
# call coverage report with fail-under=90
runs-on: ubuntu-latest
needs: [tests, fuzzing]

steps:
- uses: actions/checkout@v4

- name: Set up Python 3.11
uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"

- name: Install coverage
run: pip install coverage

- name: Download coverage artifacts
uses: actions/download-artifact@v4
with:
pattern: coverage-files-*
path: coverage-files

- name: Combine coverage
run: |
coverage combine coverage-files/**/.coverage
- name: Coverage report
# coverage report and fail if coverage is too low
run: |
coverage report --fail-under=90
- name: Generate coverage.xml
run: |
coverage xml
- name: Upload coverage artifacts
# upload coverage sqlite db for debugging
# upload coverage.xml artifact for downstream codecov upload action
uses: actions/upload-artifact@v4
with:
name: coverage-artifacts
include-hidden-files: true
path: |
.coverage
coverage.xml
if-no-files-found: error

upload-coverage:
# upload coverage to the codecov app
runs-on: ubuntu-latest
needs: [coverage-report]

steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: coverage-artifacts

- name: Upload Coverage
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.xml
2 changes: 1 addition & 1 deletion FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1 +1 @@
custom: https://etherscan.io/address/0x70CCBE10F980d80b7eBaab7D2E3A73e87D67B775
custom: ["https://etherscan.io/address/0x70CCBE10F980d80b7eBaab7D2E3A73e87D67B775", "https://giveth.io/project/vyper"]
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ make dev-init
./quicktest.sh -m "not fuzzing"
```

## Testing (with hevm)

Install hevm by downloading it from the releases page (https://github.com/ethereum/hevm/releases/latest) and making sure it is in your PATH. hevm tests can be enabled with `--hevm` flag, and hevm tests can be selected with the `-m hevm` marker. For instance, `./quicktest.sh -m "hevm" --hevm`.

## Developing (working on the compiler)

A useful script to have in your PATH is something like the following:
Expand All @@ -67,7 +71,9 @@ To run a python performance profile (to find compiler perf hotspots):
PYTHONPATH=. python -m cProfile -s tottime vyper/cli/vyper_compile.py "$@"
```

To get a call graph from a python profile, https://stackoverflow.com/a/23164271/ is helpful.
To get a call graph from a python profile, pip install `gprof2dot` and `xdot`, and run it like `gprof2dot -f pstats stats | xdot -`. (See https://stackoverflow.com/a/23164271/).

The utility timer functions `timeit`, `profileit` and `cumtimeit` are available in `vyper/utils.py`.


# Contributing
Expand Down
13 changes: 13 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,23 @@ line_length = 100
[tool:pytest]
addopts = -n auto
--dist worksteal
--strict-markers
python_files = test_*.py
testpaths = tests
xfail_strict = true
markers =
fuzzing: Run Hypothesis fuzz test suite (deselect with '-m "not fuzzing"')
requires_evm_version(version): Mark tests that require at least a specific EVM version and would throw `EvmVersionException` otherwise
venom_xfail: mark a test case as a regression (expected to fail) under the venom pipeline
hevm: run tests marked for symbolic execution

[coverage:run]
branch = True
source = vyper

# this is not available on the CI step that performs `coverage combine`
omit = vyper/version.py

# allow `coverage combine` to combine reports from heterogeneous OSes.
# (mainly important for consolidating coverage reports in the CI).
relative_files = True
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from eth_keys.datatypes import PrivateKey
from hexbytes import HexBytes

import tests.hevm
import vyper.evm.opcodes as evm_opcodes
from tests.evm_backends.base_env import BaseEnv, ExecutionReverted
from tests.evm_backends.pyevm_env import PyEvmEnv
Expand Down Expand Up @@ -40,6 +41,7 @@ def pytest_addoption(parser):
parser.addoption("--enable-compiler-debug-mode", action="store_true")
parser.addoption("--experimental-codegen", action="store_true")
parser.addoption("--tracing", action="store_true")
parser.addoption("--hevm", action="store_true")

parser.addoption(
"--evm-version",
Expand Down Expand Up @@ -114,6 +116,13 @@ def evm_version(pytestconfig):
return pytestconfig.getoption("evm_version")


@pytest.fixture(scope="session", autouse=True)
def set_hevm(pytestconfig):
flag_value = pytestconfig.getoption("hevm")
assert isinstance(flag_value, bool)
tests.hevm.HAS_HEVM = flag_value


@pytest.fixture(scope="session")
def evm_backend(pytestconfig):
backend_str = pytestconfig.getoption("evm_backend")
Expand Down
42 changes: 41 additions & 1 deletion tests/functional/builtins/codegen/test_ecrecover.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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)
19 changes: 15 additions & 4 deletions tests/functional/builtins/codegen/test_slice.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from vyper.compiler import compile_code
from vyper.compiler.settings import OptimizationLevel, Settings
from vyper.evm.opcodes import version_check
from vyper.exceptions import ArgumentException, CompilerPanic, TypeMismatch
from vyper.exceptions import (
ArgumentException,
CompilerPanic,
StaticAssertionException,
TypeMismatch,
)

_fun_bytes32_bounds = [(0, 32), (3, 29), (27, 5), (0, 5), (5, 3), (30, 2)]

Expand Down Expand Up @@ -533,9 +538,15 @@ def do_slice():

@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()
try:
c = get_contract(bad_code)
with tx_failed():
c.do_slice()
except StaticAssertionException:
# it should be ok if we
# catch the assert in compile time
# since it supposed to be revert
pass


# tests all 3 adhoc locations: `msg.data`, `self.code`, `<address>.code`
Expand Down
2 changes: 0 additions & 2 deletions tests/functional/codegen/features/test_clampers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from eth_utils import keccak

from tests.utils import ZERO_ADDRESS, decimal_to_int
from vyper.exceptions import StackTooDeep
from vyper.utils import int_bounds


Expand Down Expand Up @@ -502,7 +501,6 @@ def foo(b: DynArray[int128, 10]) -> DynArray[int128, 10]:


@pytest.mark.parametrize("value", [0, 1, -1, 2**127 - 1, -(2**127)])
@pytest.mark.venom_xfail(raises=StackTooDeep, reason="stack scheduler regression")
def test_multidimension_dynarray_clamper_passing(get_contract, value):
code = """
@external
Expand Down
Loading

0 comments on commit da79364

Please sign in to comment.