Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 5 additions & 4 deletions hathor/nanocontracts/custom_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,11 @@ def filter(function: None | Callable[[T], object], iterable: Iterable[T]) -> Ite
# XXX: used to raise SystemExit exception to close the process, we could make it raise a NCFail
'exit',

# XXX: floats are not allowed in runtime
# O(1)
# type float
'float',

# XXX: used to dynamically get an attribute, must not be allowed
'getattr',

Expand Down Expand Up @@ -581,10 +586,6 @@ def filter(function: None | Callable[[T], object], iterable: Iterable[T]) -> Ite
# (function: Callable[[T], Any], iterable: Iterable[T], /) -> filter(Iterator[T])
'filter': builtins.filter,

# O(1)
# type float
'float': builtins.float,

# O(N) for N=len(value)
# (value: object, format_spec: str = "", /) -> str
'format': builtins.format,
Expand Down
12 changes: 12 additions & 0 deletions hathor/verification/on_chain_blueprint_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,18 @@ def visit_AsyncFor(self, node: ast.AsyncFor) -> None:
def visit_AsyncWith(self, node: ast.AsyncWith) -> None:
raise SyntaxError('Async contexts are not allowed.')

def visit_Constant(self, node: ast.Constant) -> None:
match node.value:
case float():
raise SyntaxError('Float literals are not allowed.')
case complex():
raise SyntaxError('Complex literals are not allowed.')
case _:
self.generic_visit(node)

def visit_Div(self, node: ast.Div) -> None:
raise SyntaxError('Simple / division results in float, use // instead.')


class _SearchName(ast.NodeVisitor):
def __init__(self, name: str) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,32 @@ def test_forbid_async_fn(self) -> None:
syntax_errors=('Async functions are not allowed.',)
)

def test_forbid_float_literal(self) -> None:
self._test_forbid_syntax(
'a = 3.14',
syntax_errors=('Float literals are not allowed.',)
)
self._test_forbid_syntax(
'a = 3.',
syntax_errors=('Float literals are not allowed.',)
)
self._test_forbid_syntax(
'a = .14',
syntax_errors=('Float literals are not allowed.',)
)

def test_forbid_complex_literal(self) -> None:
self._test_forbid_syntax(
'a = 1j',
syntax_errors=('Complex literals are not allowed.',)
)

def test_forbid_float_division(self) -> None:
self._test_forbid_syntax(
'a = 1 / 2',
syntax_errors=('Simple / division results in float, use // instead.',)
)

def test_forbid_await_syntax(self) -> None:
# XXX: it is normally forbidden to use await outside an async context, and since async functions cannot be
# defined, it isn't possible to make a realistic code that will fail with await (also applies to other
Expand Down
5 changes: 2 additions & 3 deletions tests/nanocontracts/test_blueprints/bet.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from math import floor
from typing import Optional, TypeAlias

from hathor.nanocontracts.blueprint import Blueprint
Expand Down Expand Up @@ -221,5 +220,5 @@ def get_winner_amount(self, address: Address) -> Amount:
if result_total == 0:
return Amount(0)
address_total = self.bets_address.get((self.final_result, address), 0)
percentage = address_total / result_total
return Amount(floor(percentage * self.total))
winner_amount = Amount(address_total * self.total // result_total)
return winner_amount