Repository: crytic/slither

Issue:
Long integer constant extraction is broken
I stumbled upon this while debugging a problem with constant extraction in echidna. If a long constant is present in Solidity code, slither has trouble extracting it correctly. For instance,

```
$ cat constants.sol
contract Constants {
  bool found = false;

  function find(int i) public {
    if (i == 11234567890123456789012345678901234567) {
      found = true;
    }
  }

  function echidna_found() public view returns (bool) {
    return !found;
  }
}

$ slither --version
0.6.11

$ solc --version
solc, the solidity compiler commandline interface
Version: 0.5.0+commit.1d4f565a.Linux.g++

$ slither --print echidna constants.sol
Compilation warnings/errors on constants.sol:
constants.sol:1:1: Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.5.0;"
contract Constants {
^ (Relevant source part starts here and spans across multiple lines).

INFO:Printers:{
    "payable": {},
    "timestamp": {},
    "block_number": {},
    "msg_sender": {},
    "msg_gas": {},
    "assert": {},
    "constant_functions": {
        "Constants": [
            "echidna_found()"
        ]
    },
    "constants_used": {
        "Constants": {
            "find(int256)": [
                [
                    {
                        "value": 11234567890123456527430114806283108352,
                        "type": "uint256"
                    }
                ],
                [
                    {
                        "value": true,
                        "type": "bool"
                    }
                ]
            ],
            "echidna_found()": [
                [
                    {
                        "value": false,
                        "type": "bool"
                    }
                ]
            ]
        }
    },
    "constants_used_in_binary": {
        "Constants": {
            "find(int256)": {
                "==": [
                    [
                        {
                            "value": 11234567890123456527430114806283108352,
                            "type": "uint256"
                        }
                    ]
                ]
            }
        }
    },
    "functions_relations": {
        "Constants": {
            "find(int256)": {
                "impacts": [
                    "echidna_found()"
                ],
                "is_impacted_by": []
            },
            "echidna_found()": {
                "impacts": [],
                "is_impacted_by": [
                    "find(int256)"
                ]
            }
        }
    },
    "constructors": {},
    "have_external_calls": {},
    "call_a_parameter": {},
    "use_balance": {}
}
INFO:Slither:constants.sol analyzed (1 contracts)
INFO:Slither:Use https://crytic.io/ to get access to additional detectors and Github integration
```

Note how "11234567890123456789012345678901234567" (the constant in the code) is different from "11234567890123456527430114806283108352" (the constant reported by slither)
Long integer constant extraction is broken
I stumbled upon this while debugging a problem with constant extraction in echidna. If a long constant is present in Solidity code, slither has trouble extracting it correctly. For instance,

```
$ cat constants.sol
contract Constants {
  bool found = false;

  function find(int i) public {
    if (i == 11234567890123456789012345678901234567) {
      found = true;
    }
  }

  function echidna_found() public view returns (bool) {
    return !found;
  }
}

$ slither --version
0.6.11

$ solc --version
solc, the solidity compiler commandline interface
Version: 0.5.0+commit.1d4f565a.Linux.g++

$ slither --print echidna constants.sol
Compilation warnings/errors on constants.sol:
constants.sol:1:1: Warning: Source file does not specify required compiler version! Consider adding "pragma solidity ^0.5.0;"
contract Constants {
^ (Relevant source part starts here and spans across multiple lines).

INFO:Printers:{
    "payable": {},
    "timestamp": {},
    "block_number": {},
    "msg_sender": {},
    "msg_gas": {},
    "assert": {},
    "constant_functions": {
        "Constants": [
            "echidna_found()"
        ]
    },
    "constants_used": {
        "Constants": {
            "find(int256)": [
                [
                    {
                        "value": 11234567890123456527430114806283108352,
                        "type": "uint256"
                    }
                ],
                [
                    {
                        "value": true,
                        "type": "bool"
                    }
                ]
            ],
            "echidna_found()": [
                [
                    {
                        "value": false,
                        "type": "bool"
                    }
                ]
            ]
        }
    },
    "constants_used_in_binary": {
        "Constants": {
            "find(int256)": {
                "==": [
                    [
                        {
                            "value": 11234567890123456527430114806283108352,
                            "type": "uint256"
                        }
                    ]
                ]
            }
        }
    },
    "functions_relations": {
        "Constants": {
            "find(int256)": {
                "impacts": [
                    "echidna_found()"
                ],
                "is_impacted_by": []
            },
            "echidna_found()": {
                "impacts": [],
                "is_impacted_by": [
                    "find(int256)"
                ]
            }
        }
    },
    "constructors": {},
    "have_external_calls": {},
    "call_a_parameter": {},
    "use_balance": {}
}
INFO:Slither:constants.sol analyzed (1 contracts)
INFO:Slither:Use https://crytic.io/ to get access to additional detectors and Github integration
```

Note how "11234567890123456789012345678901234567" (the constant in the code) is different from "11234567890123456527430114806283108352" (the constant reported by slither)


Discussion:
Hi @elopez 

Thanks for the report, I will investigate this
Hi @elopez 

Thanks for the report, I will investigate this

Resolving patch:
diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py
--- a/slither/printers/guidance/echidna.py
+++ b/slither/printers/guidance/echidna.py
@@ -129,7 +129,10 @@ def as_dict(self):
 
 @json_serializable
 class ConstantValue(NamedTuple):
-    value: Union[str, int, bool]
+    # Here value should be  Union[str, int, bool]
+    # But the json lib in Echidna does not handle large integer in json
+    # So we convert everything to string
+    value: str
     type: str
 
 
@@ -141,17 +144,17 @@ def _extract_constants_from_irs(irs: List[Operation],
         if isinstance(ir, Binary):
             for r in ir.read:
                 if isinstance(r, Constant):
-                    all_cst_used_in_binary[BinaryType.str(ir.type)].append(ConstantValue(r.value, str(r.type)))
+                    all_cst_used_in_binary[BinaryType.str(ir.type)].append(ConstantValue(str(r.value), str(r.type)))
         if isinstance(ir, TypeConversion):
             if isinstance(ir.variable, Constant):
-                all_cst_used.append(ConstantValue(ir.variable.value, str(ir.type)))
+                all_cst_used.append(ConstantValue(str(ir.variable.value), str(ir.type)))
                 continue
         for r in ir.read:
             # Do not report struct_name in a.struct_name
             if isinstance(ir, Member):
                 continue
             if isinstance(r, Constant):
-                all_cst_used.append(ConstantValue(r.value, str(r.type)))
+                all_cst_used.append(ConstantValue(str(r.value), str(r.type)))
             if isinstance(r, StateVariable):
                 if r.node_initialization:
                     if r.node_initialization.irs:
diff --git a/slither/slithir/variables/constant.py b/slither/slithir/variables/constant.py
--- a/slither/slithir/variables/constant.py
+++ b/slither/slithir/variables/constant.py
@@ -1,3 +1,5 @@
+from decimal import Decimal
+
 from .variable import SlithIRVariable
 from slither.core.solidity_types.elementary_type import ElementaryType, Int, Uint
 from slither.utils.arithmetic import convert_subdenomination
@@ -23,12 +25,12 @@ def __init__(self, val, type=None, subdenomination=None):
                 else:
                     if 'e' in val:
                         base, expo = val.split('e')
-                        self._val = int(float(base)* (10 ** int(expo)))
+                        self._val = int(Decimal(base) * (10 ** int(expo)))
                     elif 'E' in val:
                         base, expo = val.split('E')
-                        self._val = int(float(base) * (10 ** int(expo)))
+                        self._val = int(Decimal(base) * (10 ** int(expo)))
                     else:
-                        self._val = int(float(val))
+                        self._val = int(Decimal(val))
             elif type.type == 'bool':
                 self._val = (val == 'true') | (val == 'True')
             else:
@@ -36,7 +38,7 @@ def __init__(self, val, type=None, subdenomination=None):
         else:
             if val.isdigit():
                 self._type = ElementaryType('uint256')
-                self._val = int(val)
+                self._val = int(Decimal(val))
             else:
                 self._type = ElementaryType('string')
                 self._val = val
diff --git a/slither/utils/arithmetic.py b/slither/utils/arithmetic.py
--- a/slither/utils/arithmetic.py
+++ b/slither/utils/arithmetic.py
@@ -1,3 +1,5 @@
+from decimal import Decimal
+
 from slither.exceptions import SlitherException
 
 
@@ -5,9 +7,9 @@ def convert_subdenomination(value, sub):
 
     # to allow 0.1 ether conversion
     if value[0:2] == "0x":
-        value = float(int(value, 16))
+        value = Decimal(int(value, 16))
     else:
-        value = float(value)
+        value = Decimal(value)
     if sub == 'wei':
         return int(value)
     if sub == 'szabo':


Test patch: