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
cyberthirst committed Mar 21, 2024
2 parents 73a4a4b + 05ec5a1 commit 2a89d9d
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 22 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,10 @@ jobs:
opt-mode: gas
evm-version: shanghai

# enable when py-evm makes it work:
#- python-version: ["3.11", "311"]
# debug: false
# opt-mode: gas
# evm-version: cancun
- python-version: ["3.11", "311"]
debug: false
opt-mode: gas
evm-version: cancun

# run with `--memorymock`, but only need to do it one configuration
# TODO: consider removing the memorymock tests
Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
"pytest-instafail>=0.4,<1.0",
"pytest-xdist>=3.5,<4.0",
"pytest-split>=0.7.0,<1.0",
"eth-tester[py-evm]>=0.9.0b1,<0.10",
"eth-tester[py-evm]>=0.10.0b4,<0.11",
"eth_abi>=4.0.0,<5.0.0",
"py-evm>=0.7.0a1,<0.8",
"py-evm>=0.10.0b4,<0.11",
"web3==6.0.0",
"lark==1.1.9",
"hypothesis[lark]>=6.0,<7.0",
"eth-stdlib==0.2.7",
"setuptools",
"typing_extensions", # we can remove this once dependencies upgrade to eth-rlp>=2.0
],
"lint": [
"black==23.12.0",
Expand Down
41 changes: 41 additions & 0 deletions tests/functional/codegen/modules/test_flag_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
def test_import_flag_types(make_input_bundle, get_contract):
lib1 = """
import lib2
flag Roles:
ADMIN
USER
enum Roles2:
ADMIN
USER
role: Roles
role2: Roles2
role3: lib2.Roles3
"""
lib2 = """
flag Roles3:
ADMIN
USER
NOBODY
"""
contract = """
import lib1
initializes: lib1
@external
def bar(r: lib1.Roles, r2: lib1.Roles2, r3: lib1.lib2.Roles3) -> bool:
lib1.role = r
lib1.role2 = r2
lib1.role3 = r3
assert lib1.role == lib1.Roles.ADMIN
assert lib1.role2 == lib1.Roles2.USER
assert lib1.role3 == lib1.lib2.Roles3.NOBODY
return True
"""

input_bundle = make_input_bundle({"lib1.vy": lib1, "lib2.vy": lib2})
c = get_contract(contract, input_bundle=input_bundle)
assert c.bar(1, 2, 4) is True
39 changes: 33 additions & 6 deletions tests/unit/cli/storage_layout/test_storage_layout.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,21 @@
from vyper.compiler import compile_code
from vyper.evm.opcodes import version_check


def _adjust_storage_layout_for_cancun(layout):
def _go(layout):
for _varname, item in layout.items():
if "slot" in item and isinstance(item["slot"], int):
item["slot"] -= 1
else:
# recurse to submodule
_go(item)

if version_check(begin="cancun"):
layout["transient_storage_layout"] = {
"$.nonreentrant_key": layout["storage_layout"].pop("$.nonreentrant_key")
}
_go(layout["storage_layout"])


def test_storage_layout():
Expand Down Expand Up @@ -40,13 +57,18 @@ def public_foo3():

out = compile_code(code, output_formats=["layout"])

assert out["layout"]["storage_layout"] == {
"$.nonreentrant_key": {"slot": 0, "type": "nonreentrant lock"},
"foo": {"slot": 1, "type": "HashMap[address, uint256]"},
"arr": {"slot": 2, "type": "DynArray[uint256, 3]"},
"baz": {"slot": 6, "type": "Bytes[65]"},
"bar": {"slot": 10, "type": "uint256"},
expected = {
"storage_layout": {
"$.nonreentrant_key": {"slot": 0, "type": "nonreentrant lock"},
"foo": {"slot": 1, "type": "HashMap[address, uint256]"},
"arr": {"slot": 2, "type": "DynArray[uint256, 3]"},
"baz": {"slot": 6, "type": "Bytes[65]"},
"bar": {"slot": 10, "type": "uint256"},
}
}
_adjust_storage_layout_for_cancun(expected)

assert out["layout"] == expected


def test_storage_and_immutables_layout():
Expand All @@ -71,6 +93,7 @@ def __init__():
"name": {"slot": 1, "type": "String[32]"},
},
}
_adjust_storage_layout_for_cancun(expected_layout)

out = compile_code(code, output_formats=["layout"])
assert out["layout"] == expected_layout
Expand Down Expand Up @@ -120,6 +143,7 @@ def __init__():
"a_library": {"supply": {"slot": 3, "type": "uint256"}},
},
}
_adjust_storage_layout_for_cancun(expected_layout)

out = compile_code(code, input_bundle=input_bundle, output_formats=["layout"])
assert out["layout"] == expected_layout
Expand Down Expand Up @@ -169,6 +193,7 @@ def __init__():
"counter2": {"slot": 3, "type": "uint256"},
},
}
_adjust_storage_layout_for_cancun(expected_layout)

out = compile_code(code, input_bundle=input_bundle, output_formats=["layout"])
assert out["layout"] == expected_layout
Expand Down Expand Up @@ -253,6 +278,7 @@ def bar():
"a_library": {"supply": {"slot": 4, "type": "uint256"}},
},
}
_adjust_storage_layout_for_cancun(expected_layout)

out = compile_code(code, input_bundle=input_bundle, output_formats=["layout"])
assert out["layout"] == expected_layout
Expand Down Expand Up @@ -334,6 +360,7 @@ def foo() -> uint256:
"counter2": {"slot": 4, "type": "uint256"},
},
}
_adjust_storage_layout_for_cancun(expected_layout)

out = compile_code(code, input_bundle=input_bundle, output_formats=["layout"])
assert out["layout"] == expected_layout
12 changes: 10 additions & 2 deletions tests/unit/semantics/test_storage_slots.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import pytest

from vyper.evm.opcodes import version_check
from vyper.exceptions import StorageLayoutException

code = """
Expand Down Expand Up @@ -97,10 +98,17 @@ def test_reentrancy_lock(get_contract):


def test_allocator_overflow(get_contract):
code = """
# --> global nonreentrancy slot allocated here <--
# cancun allocates reeentrancy slot in transient storage,
# so allocate an actual storage variable
if version_check(begin="cancun"):
slot1 = "x: uint256"
else:
slot1 = "# --> global nonreentrancy slot allocated here <--"
code = f"""
{slot1}
y: uint256[max_value(uint256)]
"""

with pytest.raises(
StorageLayoutException,
match=f"Invalid storage slot, tried to allocate slots 1 through {2**256}",
Expand Down
2 changes: 1 addition & 1 deletion vyper/ast/grammar.lark
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ tuple_def: "(" ( NAME | array_def | dyn_array_def | tuple_def ) ( "," ( NAME | a
// NOTE: Map takes a basic type and maps to another type (can be non-basic, including maps)
_MAP: "HashMap"
map_def: _MAP "[" ( NAME | array_def ) "," type "]"
imported_type: NAME "." NAME
imported_type: NAME ("." NAME)+
type: ( NAME | imported_type | array_def | tuple_def | map_def | dyn_array_def )

// Structs can be composed of 1+ basic types or other custom_types
Expand Down
9 changes: 3 additions & 6 deletions vyper/codegen/expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,9 @@ def parse_Name(self):
def parse_Attribute(self):
typ = self.expr._metadata["type"]

# MyFlag.foo
if (
isinstance(typ, FlagT)
and isinstance(self.expr.value, vy_ast.Name)
and typ.name == self.expr.value.id
):
# check if we have a flag constant, e.g.
# [lib1].MyFlag.FOO
if isinstance(typ, FlagT) and is_type_t(self.expr.value._metadata["type"], FlagT):
# 0, 1, 2, .. 255
flag_id = typ._flag_members[self.expr.attr]
value = 2**flag_id # 0 => 0001, 1 => 0010, 2 => 0100, etc.
Expand Down
1 change: 1 addition & 0 deletions vyper/semantics/analysis/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,7 @@ def _validate_self_namespace():

def visit_FlagDef(self, node):
obj = FlagT.from_FlagDef(node)
node._metadata["flag_type"] = obj
self.namespace[node.name] = obj

def visit_EventDef(self, node):
Expand Down
8 changes: 8 additions & 0 deletions vyper/semantics/types/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ def __init__(self, module: vy_ast.Module, name: Optional[str] = None):
# add the type of the event so it can be used in call position
self.add_member(e.name, TYPE_T(e._metadata["event_type"])) # type: ignore

for f in self.flag_defs:
self.add_member(f.name, TYPE_T(f._metadata["flag_type"]))
self._helper.add_member(f.name, TYPE_T(f._metadata["flag_type"]))

for s in self.struct_defs:
# add the type of the struct so it can be used in call position
self.add_member(s.name, TYPE_T(s._metadata["struct_type"])) # type: ignore
Expand Down Expand Up @@ -347,6 +351,10 @@ def function_defs(self):
def event_defs(self):
return self._module.get_children(vy_ast.EventDef)

@cached_property
def flag_defs(self):
return self._module.get_children(vy_ast.FlagDef)

@property
def struct_defs(self):
return self._module.get_children(vy_ast.StructDef)
Expand Down

0 comments on commit 2a89d9d

Please sign in to comment.