diff --git a/hathor/nanocontracts/custom_builtins.py b/hathor/nanocontracts/custom_builtins.py index abb40ac02..f6e16df9f 100644 --- a/hathor/nanocontracts/custom_builtins.py +++ b/hathor/nanocontracts/custom_builtins.py @@ -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', @@ -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, diff --git a/hathor/verification/on_chain_blueprint_verifier.py b/hathor/verification/on_chain_blueprint_verifier.py index 0e800a916..a480b0f10 100644 --- a/hathor/verification/on_chain_blueprint_verifier.py +++ b/hathor/verification/on_chain_blueprint_verifier.py @@ -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: diff --git a/tests/nanocontracts/on_chain_blueprints/test_script_restrictions.py b/tests/nanocontracts/on_chain_blueprints/test_script_restrictions.py index 1a5c1753f..f4e222e74 100644 --- a/tests/nanocontracts/on_chain_blueprints/test_script_restrictions.py +++ b/tests/nanocontracts/on_chain_blueprints/test_script_restrictions.py @@ -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 diff --git a/tests/nanocontracts/test_blueprints/bet.py b/tests/nanocontracts/test_blueprints/bet.py index 01178f116..861da7183 100644 --- a/tests/nanocontracts/test_blueprints/bet.py +++ b/tests/nanocontracts/test_blueprints/bet.py @@ -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 @@ -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