Replace Decimal with Fraction

Fractions are arbitrary-precision and will not cause truncated values.
pull/1377/head
Emilio López 2 years ago
parent 98611e3ae7
commit 036f7d62f4
  1. 4
      slither/slithir/variables/constant.py
  2. 3
      slither/utils/__init__.py
  3. 12
      slither/utils/arithmetic.py
  4. 10
      slither/utils/integer_conversion.py
  5. 6
      slither/visitors/expression/constants_folding.py

@ -1,4 +1,4 @@
from decimal import Decimal from fractions import Fraction
from functools import total_ordering from functools import total_ordering
from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint
@ -33,7 +33,7 @@ class Constant(SlithIRVariable):
else: else:
if val.isdigit(): if val.isdigit():
self._type = ElementaryType("uint256") self._type = ElementaryType("uint256")
self._val = int(Decimal(val)) self._val = int(Fraction(val))
else: else:
self._type = ElementaryType("string") self._type = ElementaryType("string")
self._val = val self._val = val

@ -1,3 +0,0 @@
from slither.utils.arithmetic import configure_decimal_precision
configure_decimal_precision()

@ -1,13 +1,7 @@
from decimal import Decimal, getcontext from fractions import Fraction
from slither.exceptions import SlitherException from slither.exceptions import SlitherException
def configure_decimal_precision():
# Configure decimal precision high enough for 2 ** 256
# Otherwise, large constant arithmetic may get truncated
if getcontext().prec < 80:
getcontext().prec = 80
# pylint: disable=too-many-branches # pylint: disable=too-many-branches
def convert_subdenomination( def convert_subdenomination(
value: str, sub: str value: str, sub: str
@ -15,9 +9,9 @@ def convert_subdenomination(
# to allow 0.1 ether conversion # to allow 0.1 ether conversion
if value[0:2] == "0x": if value[0:2] == "0x":
decimal_value = Decimal(int(value, 16)) decimal_value = Fraction(int(value, 16))
else: else:
decimal_value = Decimal(value) decimal_value = Fraction(value)
if sub == "wei": if sub == "wei":
return int(decimal_value) return int(decimal_value)
if sub == "gwei": if sub == "gwei":

@ -1,4 +1,4 @@
from decimal import Decimal from fractions import Fraction
from typing import Union from typing import Union
from slither.exceptions import SlitherError from slither.exceptions import SlitherError
@ -12,17 +12,17 @@ def convert_string_to_int(val: Union[str, int]) -> int:
if "e" in val or "E" in val: if "e" in val or "E" in val:
base, expo = val.split("e") if "e" in val else val.split("E") base, expo = val.split("e") if "e" in val else val.split("E")
base, expo = Decimal(base), int(expo) base, expo = Fraction(base), int(expo)
# The resulting number must be < 2**256-1, otherwise solc # The resulting number must be < 2**256-1, otherwise solc
# Would not be able to compile it # Would not be able to compile it
# 10**77 is the largest exponent that fits # 10**77 is the largest exponent that fits
# See https://github.com/ethereum/solidity/blob/9e61f92bd4d19b430cb8cb26f1c7cf79f1dff380/libsolidity/ast/Types.cpp#L1281-L1290 # See https://github.com/ethereum/solidity/blob/9e61f92bd4d19b430cb8cb26f1c7cf79f1dff380/libsolidity/ast/Types.cpp#L1281-L1290
if expo > 77: if expo > 77:
if base != Decimal(0): if base != Fraction(0):
raise SlitherError( raise SlitherError(
f"{base}e{expo} is too large to fit in any Solidity integer size" f"{base}e{expo} is too large to fit in any Solidity integer size"
) )
return 0 return 0
return int(Decimal(base) * Decimal(10**expo)) return int(Fraction(base) * Fraction(10**expo))
return int(Decimal(val)) return int(Fraction(val))

@ -1,4 +1,4 @@
from decimal import Decimal, InvalidOperation from fractions import Fraction
from slither.core.expressions import BinaryOperationType, Literal, UnaryOperationType from slither.core.expressions import BinaryOperationType, Literal, UnaryOperationType
from slither.utils.integer_conversion import convert_string_to_int from slither.utils.integer_conversion import convert_string_to_int
from slither.visitors.expression.expression import ExpressionVisitor from slither.visitors.expression.expression import ExpressionVisitor
@ -79,8 +79,8 @@ class ConstantFolding(ExpressionVisitor):
def _post_literal(self, expression): def _post_literal(self, expression):
try: try:
set_val(expression, Decimal(expression.value)) set_val(expression, Fraction(expression.value))
except InvalidOperation: except ValueError:
raise NotConstant raise NotConstant
def _post_assignement_operation(self, expression): def _post_assignement_operation(self, expression):

Loading…
Cancel
Save