From 162d6b3a87c4ac88beef68724506dce34a2fc873 Mon Sep 17 00:00:00 2001 From: francois Date: Fri, 10 Nov 2023 23:19:45 +0100 Subject: [PATCH] add more robust fuzzing, better slice errors --- .../functional/builtins/codegen/test_slice.py | 20 ++++++ .../functional/builtins/folding/test_slice.py | 67 +++++++++++++++++-- vyper/builtins/functions.py | 1 - 3 files changed, 82 insertions(+), 6 deletions(-) diff --git a/tests/functional/builtins/codegen/test_slice.py b/tests/functional/builtins/codegen/test_slice.py index 1717605c5e..6369b39c7e 100644 --- a/tests/functional/builtins/codegen/test_slice.py +++ b/tests/functional/builtins/codegen/test_slice.py @@ -2,6 +2,8 @@ import pytest from hypothesis import given, settings +from vyper import ast as vy_ast +from vyper.builtins import functions as vy_fn from vyper.compiler.settings import OptimizationLevel from vyper.exceptions import ArgumentException, TypeMismatch @@ -473,3 +475,21 @@ def test_comptime(get_contract, code, result): assert ret.hex() == result else: assert ret == result + + +error_slice = [ + "slice(0x00, 0, 1)", + 'slice("why hello! how are you?", 32, 1)', + 'slice("why hello! how are you?", -1, 1)', + 'slice("why hello! how are you?", 4, 0)', + 'slice("why hello! how are you?", 0, 33)', + 'slice("why hello! how are you?", 16, 17)', +] + + +@pytest.mark.parametrize("code", error_slice) +def test_slice_error(code): + vyper_ast = vy_ast.parse_to_ast(code) + old_node = vyper_ast.body[0].value + with pytest.raises(ArgumentException): + vy_fn.DISPATCH_TABLE["slice"].evaluate(old_node) diff --git a/tests/functional/builtins/folding/test_slice.py b/tests/functional/builtins/folding/test_slice.py index 1f8edad211..8ad759a483 100644 --- a/tests/functional/builtins/folding/test_slice.py +++ b/tests/functional/builtins/folding/test_slice.py @@ -1,3 +1,5 @@ +import string + import pytest from hypothesis import given, settings from hypothesis import strategies as st @@ -7,6 +9,10 @@ from vyper.exceptions import ArgumentException +def normalize_bytes(data): + return bytes(int.from_bytes(data, "big")) + + @pytest.mark.fuzzing @settings(max_examples=50) @given( @@ -33,11 +39,7 @@ def foo(a: bytes32) -> Bytes[{le}]: s *= 2 le *= 2 - assert ( - int.from_bytes(contract.foo(a), "big") - == int.from_bytes(new_node.value, "big") - == int(a[2:][s : (s + le)], 16) - ) + assert normalize_bytes(contract.foo(a)) == new_node.value == bytes.fromhex(a[2:][s : (s + le)]) @pytest.mark.fuzzing @@ -61,3 +63,58 @@ def test_slice_bytesnot32(a, s, le): old_node = vyper_ast.body[0].value with pytest.raises(ArgumentException): vy_fn.DISPATCH_TABLE["slice"].evaluate(old_node) + + +@pytest.mark.fuzzing +@settings(max_examples=50) +@given( + a=st.binary(min_size=1, max_size=100), + s=st.integers(min_value=0, max_value=99), + le=st.integers(min_value=1, max_value=100), +) +def test_slice_dynbytes(get_contract, a, s, le): + s = s % len(a) + le = min(len(a), len(a) - s, le) + + source = f""" +@external +def foo(a: Bytes[100]) -> Bytes[{le}]: + return slice(a, {s}, {le}) + """ + contract = get_contract(source) + + vyper_ast = vy_ast.parse_to_ast(f"slice({a}, {s}, {le})") + old_node = vyper_ast.body[0].value + new_node = vy_fn.DISPATCH_TABLE["slice"].evaluate(old_node) + + assert contract.foo(a) == new_node.value == a[s : (s + le)] + + +valid_char = [ + char for char in string.printable if char not in (string.whitespace.replace(" ", "") + '"\\') +] + + +@pytest.mark.fuzzing +@settings(max_examples=50) +@given( + a=st.text(alphabet=valid_char, min_size=1, max_size=100), + s=st.integers(min_value=0, max_value=99), + le=st.integers(min_value=1, max_value=100), +) +def test_slice_string(get_contract, a, s, le): + s = s % len(a) + le = min(len(a), len(a) - s, le) + + source = f""" +@external +def foo(a: String[100]) -> String[{le}]: + return slice(a, {s}, {le}) + """ + contract = get_contract(source) + + vyper_ast = vy_ast.parse_to_ast(f'slice("{a}", {s}, {le})') + old_node = vyper_ast.body[0].value + new_node = vy_fn.DISPATCH_TABLE["slice"].evaluate(old_node) + + assert contract.foo(a) == new_node.value == a[s : (s + le)] diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py index dce3b6ad8e..747e95ea98 100644 --- a/vyper/builtins/functions.py +++ b/vyper/builtins/functions.py @@ -318,7 +318,6 @@ def evaluate(self, node): else: length = len(lit.value) // 2 - 1 if length != 32: - # raise UnfoldableNode raise ArgumentException("Length can only be of 32", lit) st_val *= 2 le_val *= 2