-
-
Notifications
You must be signed in to change notification settings - Fork 823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat[test]: add more transient storage tests #3883
Changes from 12 commits
4820822
d8ec500
ee7c5ed
5c9d3c4
78fb377
52adc7b
419dd0c
2257b21
329431e
68a26e3
2d0a8df
31d9af9
f66ade2
82ed420
0d05989
ef1b00b
19038ee
51ec461
4be6e3d
70e5bc5
4bc949f
ee13d8f
ed7629e
0d96ef6
f58c85d
6bc88ca
d0c5751
5de3564
9b4c209
07bfffa
8ee1061
2948300
d3c5b07
3e8f540
7e57b4a
cccce57
394ae32
e2356c7
2f1b4f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,8 @@ | |
from vyper.codegen.ir_node import IRnode | ||
from vyper.compiler.input_bundle import FilesystemInputBundle, InputBundle | ||
from vyper.compiler.settings import OptimizationLevel, Settings, _set_debug_mode | ||
from vyper.evm.opcodes import version_check | ||
from vyper.exceptions import TransientStorageException | ||
from vyper.ir import compile_ir, optimizer | ||
from vyper.utils import ERC5202_PREFIX | ||
|
||
|
@@ -525,3 +527,13 @@ def fn(exception=TransactionFailed, exc_text=None): | |
assert exc_text in str(excinfo.value), (exc_text, excinfo.value) | ||
|
||
return fn | ||
|
||
|
||
@pytest.fixture(autouse=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'm not sure autouse fixtures are the best way to go about this, maybe add a pytest item collection hook? i don't remember exactly what the best practice is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. correction: that does not work, but |
||
def check_transient_marker(request): | ||
if request.node.get_closest_marker("transient") and not version_check(begin="cancun"): | ||
charles-cooper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
request.node.add_marker( | ||
pytest.mark.xfail( | ||
reason="transient storage", raises=TransientStorageException, strict=True | ||
) | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,9 @@ | ||
import pytest | ||
from eth_tester.exceptions import TransactionFailed | ||
|
||
from vyper.compiler import compile_code | ||
from vyper.evm.opcodes import version_check | ||
from vyper.exceptions import StructureException | ||
|
||
|
||
def test_transient_blocked(evm_version): | ||
# test transient is blocked on pre-cancun and compiles post-cancun | ||
code = """ | ||
my_map: transient(HashMap[address, uint256]) | ||
""" | ||
if version_check(begin="cancun"): | ||
assert compile_code(code) is not None | ||
else: | ||
with pytest.raises(StructureException): | ||
compile_code(code) | ||
from vyper.exceptions import VyperException | ||
|
||
|
||
def test_transient_compiles(): | ||
|
@@ -55,3 +44,316 @@ def setter(k: address, v: uint256): | |
|
||
charles-cooper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert "TLOAD" in t | ||
assert "TSTORE" in t | ||
|
||
|
||
@pytest.mark.transient | ||
@pytest.mark.parametrize( | ||
"typ,value,zero", | ||
[ | ||
("uint256", 42, 0), | ||
("int256", -(2**200), 0), | ||
("int128", -(2**126), 0), | ||
("address", "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", None), | ||
("bytes32", b"deadbeef" * 4, b"\x00" * 32), | ||
("bool", True, False), | ||
("String[10]", "Vyper hiss", ""), | ||
("Bytes[10]", b"Vyper hiss", b""), | ||
], | ||
) | ||
def test_value_storage_retrieval(typ, value, zero, get_contract): | ||
code = f""" | ||
bar: public(transient({typ})) | ||
|
||
@external | ||
def foo(a: {typ}) -> {typ}: | ||
self.bar = a | ||
return self.bar | ||
""" | ||
|
||
c = get_contract(code) | ||
assert c.bar() == zero | ||
assert c.foo(value) == value | ||
assert c.bar() == zero | ||
assert c.foo(value) == value | ||
assert c.bar() == zero | ||
|
||
|
||
@pytest.mark.transient | ||
@pytest.mark.parametrize("val", [0, 1, 2**256 - 1]) | ||
def test_usage_in_constructor(get_contract, val): | ||
code = """ | ||
A: transient(uint256) | ||
a: public(uint256) | ||
|
||
|
||
@deploy | ||
def __init__(_a: uint256): | ||
self.A = _a | ||
self.a = self.A | ||
|
||
|
||
@external | ||
@view | ||
def a1() -> uint256: | ||
return self.A | ||
""" | ||
|
||
c = get_contract(code, val) | ||
assert c.a() == val | ||
assert c.a1() == 0 | ||
|
||
|
||
def test_multiple_transient_values(get_contract): | ||
code = """ | ||
a: public(transient(uint256)) | ||
b: public(transient(address)) | ||
c: public(transient(String[64])) | ||
|
||
@external | ||
def foo(_a: uint256, _b: address, _c: String[64]) -> (uint256, address, String[64]): | ||
self.a = _a | ||
self.b = _b | ||
self.c = _c | ||
return self.a, self.b, self.c | ||
""" | ||
values = (3, "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", "Hello world") | ||
|
||
if version_check(begin="cancun"): | ||
charles-cooper marked this conversation as resolved.
Show resolved
Hide resolved
|
||
c = get_contract(code) | ||
assert c.foo(*values) == list(values) | ||
assert c.a() == 0 | ||
assert c.b() is None | ||
assert c.c() == "" | ||
assert c.foo(*values) == list(values) | ||
else: | ||
# multiple errors | ||
with pytest.raises(VyperException): | ||
compile_code(code) | ||
|
||
|
||
@pytest.mark.transient | ||
def test_struct_transient(get_contract): | ||
code = """ | ||
struct MyStruct: | ||
cyberthirst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
a: uint256 | ||
b: uint256 | ||
c: address | ||
d: int256 | ||
|
||
my_struct: public(transient(MyStruct)) | ||
|
||
@external | ||
def foo(_a: uint256, _b: uint256, _c: address, _d: int256) -> MyStruct: | ||
self.my_struct = MyStruct( | ||
a=_a, | ||
b=_b, | ||
c=_c, | ||
d=_d | ||
) | ||
return self.my_struct | ||
""" | ||
values = (100, 42, "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", -(2**200)) | ||
|
||
c = get_contract(code) | ||
assert c.foo(*values) == values | ||
assert c.my_struct() == (0, 0, None, 0) | ||
assert c.foo(*values) == values | ||
|
||
|
||
@pytest.mark.transient | ||
def test_complex_transient_modifiable(get_contract): | ||
code = """ | ||
struct MyStruct: | ||
a: uint256 | ||
|
||
my_struct: public(transient(MyStruct)) | ||
|
||
@external | ||
def foo(a: uint256) -> MyStruct: | ||
self.my_struct = MyStruct(a=a) | ||
|
||
# struct members are modifiable after initialization | ||
self.my_struct.a += 1 | ||
|
||
return self.my_struct | ||
""" | ||
|
||
c = get_contract(code) | ||
assert c.foo(1) == (2,) | ||
assert c.my_struct() == (0,) | ||
assert c.foo(1) == (2,) | ||
|
||
|
||
@pytest.mark.transient | ||
def test_list_transient(get_contract): | ||
code = """ | ||
my_list: public(transient(uint256[3])) | ||
|
||
@external | ||
def foo(_a: uint256, _b: uint256, _c: uint256) -> uint256[3]: | ||
self.my_list = [_a, _b, _c] | ||
return self.my_list | ||
""" | ||
values = (100, 42, 23230) | ||
|
||
c = get_contract(code) | ||
assert c.foo(*values) == list(values) | ||
for i in range(3): | ||
assert c.my_list(i) == 0 | ||
assert c.foo(*values) == list(values) | ||
|
||
|
||
@pytest.mark.transient | ||
def test_dynarray_transient(get_contract): | ||
code = """ | ||
my_list: public(transient(DynArray[uint256, 3])) | ||
|
||
@external | ||
def get_my_list(_a: uint256, _b: uint256, _c: uint256) -> DynArray[uint256, 3]: | ||
self.my_list = [_a, _b, _c] | ||
return self.my_list | ||
|
||
@external | ||
def get_idx_two(_a: uint256, _b: uint256, _c: uint256) -> uint256: | ||
self.my_list = [_a, _b, _c] | ||
return self.my_list[2] | ||
""" | ||
values = (100, 42, 23230) | ||
|
||
c = get_contract(code) | ||
assert c.get_my_list(*values) == list(values) | ||
with pytest.raises(TransactionFailed): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this on purpose instead of tx_failed()? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nope, I was not aware of that! I will change to tx_failed(). |
||
c.my_list(0) | ||
assert c.get_idx_two(*values) == values[2] | ||
with pytest.raises(TransactionFailed): | ||
c.my_list(0) | ||
|
||
|
||
@pytest.mark.transient | ||
def test_nested_dynarray_transient_2(get_contract): | ||
code = """ | ||
my_list: public(transient(DynArray[DynArray[uint256, 3], 3])) | ||
|
||
@external | ||
def get_my_list(_a: uint256, _b: uint256, _c: uint256) -> DynArray[DynArray[uint256, 3], 3]: | ||
self.my_list = [[_a, _b, _c], [_b, _a, _c], [_c, _b, _a]] | ||
return self.my_list | ||
|
||
@external | ||
def get_idx_two(_a: uint256, _b: uint256, _c: uint256) -> uint256: | ||
self.my_list = [[_a, _b, _c], [_b, _a, _c], [_c, _b, _a]] | ||
return self.my_list[2][2] | ||
""" | ||
values = (100, 42, 23230) | ||
expected_values = [[100, 42, 23230], [42, 100, 23230], [23230, 42, 100]] | ||
|
||
c = get_contract(code) | ||
assert c.get_my_list(*values) == expected_values | ||
assert c.get_idx_two(*values) == expected_values[2][2] | ||
|
||
|
||
@pytest.mark.transient | ||
def test_nested_dynarray_transient(get_contract): | ||
code = """ | ||
my_list: public(transient(DynArray[DynArray[DynArray[int128, 3], 3], 3])) | ||
|
||
@external | ||
def get_my_list(x: int128, y: int128, z: int128) -> DynArray[DynArray[DynArray[int128, 3], 3], 3]: | ||
self.my_list = [ | ||
[[x, y, z], [y, z, x], [z, y, x]], | ||
[ | ||
[x * 1000 + y, y * 1000 + z, z * 1000 + x], | ||
[- (x * 1000 + y), - (y * 1000 + z), - (z * 1000 + x)], | ||
[- (x * 1000) + y, - (y * 1000) + z, - (z * 1000) + x], | ||
], | ||
[ | ||
[z * 2, y * 3, x * 4], | ||
[z * (-2), y * (-3), x * (-4)], | ||
[z * (-y), y * (-x), x * (-z)], | ||
], | ||
] | ||
return self.my_list | ||
|
||
@external | ||
def get_idx_two(x: int128, y: int128, z: int128) -> int128: | ||
self.my_list = [ | ||
[[x, y, z], [y, z, x], [z, y, x]], | ||
[ | ||
[x * 1000 + y, y * 1000 + z, z * 1000 + x], | ||
[- (x * 1000 + y), - (y * 1000 + z), - (z * 1000 + x)], | ||
[- (x * 1000) + y, - (y * 1000) + z, - (z * 1000) + x], | ||
], | ||
[ | ||
[z * 2, y * 3, x * 4], | ||
[z * (-2), y * (-3), x * (-4)], | ||
[z * (-y), y * (-x), x * (-z)], | ||
], | ||
] | ||
return self.my_list[2][2][2] | ||
""" | ||
values = (37, 41, 73) | ||
expected_values = [ | ||
[[37, 41, 73], [41, 73, 37], [73, 41, 37]], | ||
[[37041, 41073, 73037], [-37041, -41073, -73037], [-36959, -40927, -72963]], | ||
[[146, 123, 148], [-146, -123, -148], [-2993, -1517, -2701]], | ||
] | ||
|
||
c = get_contract(code) | ||
assert c.get_my_list(*values) == expected_values | ||
with pytest.raises(TransactionFailed): | ||
c.my_list(0, 0, 0) | ||
cyberthirst marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert c.get_idx_two(*values) == expected_values[2][2][2] | ||
with pytest.raises(TransactionFailed): | ||
c.my_list(0, 0, 0) | ||
|
||
|
||
@pytest.mark.transient | ||
@pytest.mark.parametrize("n", range(5)) | ||
def test_internal_function_with_transient(get_contract, n): | ||
code = """ | ||
@internal | ||
def foo() -> uint256: | ||
self.counter += 1 | ||
return self.counter | ||
|
||
counter: uint256 | ||
val: public(transient(uint256)) | ||
|
||
@external | ||
def bar(x: uint256) -> uint256: | ||
self.counter = x | ||
self.foo() | ||
self.val = self.foo() | ||
return self.val | ||
""" | ||
|
||
c = get_contract(code) | ||
assert c.bar(n) == n + 2 | ||
assert c.val() == 0 | ||
assert c.bar(n) == n + 2 | ||
|
||
|
||
@pytest.mark.transient | ||
def test_nested_internal_function_transient(get_contract): | ||
code = """ | ||
d: public(uint256) | ||
x: public(transient(uint256)) | ||
|
||
@deploy | ||
def __init__(): | ||
self.d = 1 | ||
self.x = 2 | ||
self.a() | ||
|
||
@internal | ||
def a(): | ||
self.b() | ||
|
||
@internal | ||
def b(): | ||
self.d = self.x | ||
""" | ||
|
||
c = get_contract(code) | ||
assert c.d() == 2 | ||
assert c.x() == 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like it needs to be updated .. not sure why tests are not failing otherwise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, we don't have
--strict-markers
enabled (see note here https://docs.pytest.org/en/latest/example/markers.html#registering-markers)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we want to enable
--strict-markers
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, probably, i have an open PR which touches setup.cfg though so we should wait until that is merged to avoid conflicts