Skip to content
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

fix[lang]: disallow blockhash in pure functions #3157

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
90747ef
add blockhash to local validation
tserg Nov 24, 2022
393dfed
add test
tserg Nov 24, 2022
3d303c7
fix mypy lint
tserg Nov 24, 2022
439bb78
use StateMutability
tserg Nov 27, 2022
5fadb3b
fix lint
tserg Nov 27, 2022
d976d3f
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg Nov 28, 2022
aa82067
fix merge conflict
tserg Nov 28, 2022
cccd707
fix import
tserg Nov 28, 2022
6f193e4
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg May 18, 2023
a0e80ad
clean up imports
tserg May 18, 2023
b3e4b62
undo blank line
tserg May 18, 2023
79e1674
filter for nonpure builtins from namespace
tserg May 18, 2023
f6f2e91
fetch all call nodes
tserg May 18, 2023
a5b1924
clean up
tserg May 18, 2023
b754a1b
undo blank line
tserg May 18, 2023
eff8438
add interface test
tserg May 18, 2023
035a91b
fix lint
tserg May 18, 2023
9410901
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg Feb 21, 2024
f6c95b2
revert merge conflict
tserg Feb 21, 2024
19baa5d
some fixes
tserg Feb 21, 2024
a33c7a8
move raw_call check to fetch_call_return
tserg Mar 8, 2024
bf26d2e
fix lint
tserg Mar 8, 2024
7ae3018
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg Mar 8, 2024
c3f2bf6
fix test
tserg Mar 8, 2024
3931761
modify tests
tserg Mar 19, 2024
1895549
add default value
tserg Mar 19, 2024
9e52ecc
Merge branch 'master' into fix/blockhash
charles-cooper Mar 25, 2024
dad2c46
apply bts suggestion
tserg Mar 26, 2024
ebfe003
revert raw call changes
tserg Mar 26, 2024
4709936
fix lint
tserg Mar 26, 2024
e2e9864
Merge branch 'fix/blockhash' of https://github.com/tserg/vyper into f…
tserg Mar 26, 2024
b1517b7
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg Mar 27, 2024
d9e32c7
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg Oct 11, 2024
8610908
add blobhash
tserg Oct 11, 2024
36d6ddb
Merge branch 'master' of https://github.com/vyperlang/vyper into fix/…
tserg Oct 12, 2024
115f9c4
Merge branch 'master' into fix/blockhash
charles-cooper Dec 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions tests/functional/codegen/features/decorators/test_pure.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,35 @@ def foo() -> uint256:
),
FunctionDeclarationException,
)


def test_invalid_builtin(get_contract, assert_compile_failed):
assert_compile_failed(
lambda: get_contract(
"""
@external
@pure
def foo(x: uint256)-> bytes32:
return blockhash(x)
"""
),
StateAccessViolation,
)


def test_invalid_interface(get_contract, assert_compile_failed):
assert_compile_failed(
tserg marked this conversation as resolved.
Show resolved Hide resolved
lambda: get_contract(
"""
interface Foo:
def foo() -> uint256: payable


@external
@pure
def bar(a: address) -> uint256:
return extcall Foo(a).foo()
tserg marked this conversation as resolved.
Show resolved Hide resolved
"""
),
StateAccessViolation,
)
3 changes: 2 additions & 1 deletion vyper/builtins/_signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from vyper.codegen.expr import Expr
from vyper.codegen.ir_node import IRnode
from vyper.exceptions import CompilerPanic, TypeMismatch, UnfoldableNode
from vyper.semantics.analysis.base import Modifiability
from vyper.semantics.analysis.base import Modifiability, StateMutability

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
vyper.semantics.analysis.base
begins an import cycle.
from vyper.semantics.analysis.utils import (
check_modifiability,
get_exact_type_from_node,
Expand Down Expand Up @@ -87,6 +87,7 @@ class BuiltinFunctionT(VyperType):
_return_type: Optional[VyperType] = None
_equality_attrs = ("_id",)
_is_terminus = False
mutability: StateMutability = StateMutability.PURE

@property
def modifiability(self):
Expand Down
36 changes: 21 additions & 15 deletions vyper/builtins/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
UnfoldableNode,
ZeroDivisionException,
)
from vyper.semantics.analysis.base import Modifiability, VarInfo
from vyper.semantics.analysis.base import Modifiability, StateMutability, VarInfo

Check notice

Code scanning / CodeQL

Cyclic import Note

Import of module
vyper.semantics.analysis.base
begins an import cycle.
from vyper.semantics.analysis.utils import (
get_common_types,
get_exact_type_from_node,
Expand Down Expand Up @@ -1061,6 +1061,25 @@ def fetch_call_return(self, node):

kwargz = {i.arg: i.value for i in node.keywords}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe get i.get_folded_value().value here


delegate_call = kwargz.get("is_delegate_call")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if the user passes is_delegate_call=False?

static_call = kwargz.get("is_static_call")
if delegate_call and static_call:
raise ArgumentException(
"Call may use one of `is_delegate_call` or `is_static_call`, not both"
)

value = kwargz.get("value")
if (delegate_call or static_call) and value is not None:
charles-cooper marked this conversation as resolved.
Show resolved Hide resolved
raise ArgumentException("value= may not be passed for static or delegate calls!")

fn_node = node.get_ancestor(vy_ast.FunctionDef)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note for refactoring: ideally we should pass this in a context object

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should I add a comment here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure

fn_type = fn_node._metadata["func_type"]
if not static_call and not fn_type.is_mutable:
raise StateAccessViolation(
f"Cannot make modifying calls from {fn_type.mutability},"
" use `is_static_call=True` to perform this action"
)

outsize = kwargz.get("max_outsize")
if outsize is not None:
outsize = outsize.get_folded_value()
Expand Down Expand Up @@ -1106,20 +1125,6 @@ def build_IR(self, expr, args, kwargs, context):
kwargs["revert_on_failure"],
)

if delegate_call and static_call:
raise ArgumentException(
"Call may use one of `is_delegate_call` or `is_static_call`, not both"
)

if (delegate_call or static_call) and value.value != 0:
raise ArgumentException("value= may not be passed for static or delegate calls!")

if not static_call and context.is_constant():
raise StateAccessViolation(
f"Cannot make modifying calls from {context.pp_constancy()},"
" use `is_static_call=True` to perform this action"
)

if data.value == "~calldata":
call_ir = ["with", "mem_ofst", "msize"]
args_ofst = ["seq", ["calldatacopy", "mem_ofst", 0, "calldatasize"], "mem_ofst"]
Expand Down Expand Up @@ -1249,6 +1254,7 @@ class BlockHash(BuiltinFunctionT):
_id = "blockhash"
_inputs = [("block_num", UINT256_T)]
_return_type = BYTES32_T
mutability = StateMutability.VIEW

@process_inputs
def build_IR(self, expr, args, kwargs, contact):
Expand Down
5 changes: 4 additions & 1 deletion vyper/semantics/analysis/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -811,7 +811,10 @@ def visit_Call(self, node: vy_ast.Call, typ: VyperType) -> None:
for arg, arg_type in zip(node.args, func_type.arg_types):
self.visit(arg, arg_type)
else:
# builtin functions
# builtin functions and interfaces
if self.function_analyzer and hasattr(func_type, "mutability"):
charles-cooper marked this conversation as resolved.
Show resolved Hide resolved
self._check_call_mutability(func_type.mutability) # type: ignore

arg_types = func_type.infer_arg_types(node, expected_return_typ=typ) # type: ignore
for arg, arg_type in zip(node.args, arg_types):
self.visit(arg, arg_type)
Expand Down
Loading