diff --git a/slither/core/declarations/function.py b/slither/core/declarations/function.py index f9e0b9eda3..58f8db83c1 100644 --- a/slither/core/declarations/function.py +++ b/slither/core/declarations/function.py @@ -17,6 +17,7 @@ from slither.core.solidity_types.type import Type from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.variables.state_variable import StateVariable +from slither.utils.type import convert_type_for_solidity_signature from slither.utils.utils import unroll logger = logging.getLogger("Function") @@ -726,13 +727,6 @@ def slithir_ssa_operations(self): ################################################################################### ################################################################################### - @staticmethod - def _convert_type_for_solidity_signature(t: Type): - from slither.core.declarations import Contract - if isinstance(t, UserDefinedType) and isinstance(t.type, Contract): - return "address" - return str(t) - @property def solidity_signature(self) -> str: """ @@ -740,7 +734,7 @@ def solidity_signature(self) -> str: Contract and converted into address :return: the solidity signature """ - parameters = [self._convert_type_for_solidity_signature(x.type) for x in self.parameters] + parameters = [str(convert_type_for_solidity_signature(x.type)) for x in self.parameters] return self.name + '(' + ','.join(parameters) + ')' diff --git a/slither/core/variables/state_variable.py b/slither/core/variables/state_variable.py index af5421c53d..1b103dd952 100644 --- a/slither/core/variables/state_variable.py +++ b/slither/core/variables/state_variable.py @@ -1,6 +1,6 @@ -from .variable import Variable from slither.core.children.child_contract import ChildContract -from slither.utils.type import export_nested_types_from_variable +from .variable import Variable + class StateVariable(ChildContract, Variable): @@ -16,31 +16,6 @@ def is_declared_by(self, contract): """ return self.contract == contract - - ################################################################################### - ################################################################################### - # region Signature - ################################################################################### - ################################################################################### - - @property - def signature(self): - """ - Return the signature of the state variable as a function signature - :return: (str, list(str), list(str)), as (name, list parameters type, list return values type) - """ - return self.name, [str(x) for x in export_nested_types_from_variable(self)], self.type - - @property - def signature_str(self): - """ - Return the signature of the state variable as a function signature - :return: str: func_name(type1,type2) returns(type3) - """ - name, parameters, returnVars = self.signature - return name+'('+','.join(parameters)+') returns('+','.join(returnVars)+')' - - # endregion ################################################################################### ################################################################################### # region Name diff --git a/slither/core/variables/variable.py b/slither/core/variables/variable.py index 4d7f26e03b..1f1726ad4b 100644 --- a/slither/core/variables/variable.py +++ b/slither/core/variables/variable.py @@ -1,11 +1,13 @@ """ Variable module """ +from typing import Tuple, List from slither.core.source_mapping.source_mapping import SourceMapping from slither.core.solidity_types.type import Type from slither.core.solidity_types.elementary_type import ElementaryType + class Variable(SourceMapping): def __init__(self): @@ -78,25 +80,36 @@ def set_type(self, t): assert isinstance(t, (Type, list)) or t is None self._type = t + ################################################################################### + ################################################################################### + # region Signature + ################################################################################### + ################################################################################### + @property - def function_name(self): - ''' - Return the name of the variable as a function signature - :return: - ''' - from slither.core.solidity_types import ArrayType, MappingType - variable_getter_args = "" - if type(self.type) is ArrayType: - length = 0 - v = self - while type(v.type) is ArrayType: - length += 1 - v = v.type - variable_getter_args = ','.join(["uint256"] * length) - elif type(self.type) is MappingType: - variable_getter_args = self.type.type_from - - return f"{self.name}({variable_getter_args})" + def signature(self) -> Tuple[str, List[str], List[str]]: + """ + Return the signature of the state variable as a function signature + :return: (str, list(str), list(str)), as (name, list parameters type, list return values type) + """ + from slither.utils.type import export_nested_types_from_variable, export_return_type_from_variable + return (self.name, + [str(x) for x in export_nested_types_from_variable(self)], + [str(x) for x in export_return_type_from_variable(self)]) + + @property + def signature_str(self): + """ + Return the signature of the state variable as a function signature + :return: str: func_name(type1,type2) returns(type3) + """ + name, parameters, returnVars = self.signature + return name + '(' + ','.join(parameters) + ') returns(' + ','.join(returnVars) + ')' + + @property + def solidity_signature(self): + name, parameters, _ = self.signature + return f'{name}({",".join(parameters)})' def __str__(self): return self._name diff --git a/slither/printers/guidance/echidna.py b/slither/printers/guidance/echidna.py index 26f3221c39..f38a62a15e 100644 --- a/slither/printers/guidance/echidna.py +++ b/slither/printers/guidance/echidna.py @@ -97,7 +97,7 @@ def _extract_constant_functions(slither: Slither) -> Dict[str, List[str]]: ret: Dict[str, List[str]] = {} for contract in slither.contracts: cst_functions = [_get_name(f) for f in contract.functions_entry_points if _is_constant(f)] - cst_functions += [v.function_name for v in contract.state_variables if v.visibility in ['public']] + cst_functions += [v.solidity_signature for v in contract.state_variables if v.visibility in ['public']] if cst_functions: ret[contract.name] = cst_functions return ret diff --git a/slither/printers/summary/function_ids.py b/slither/printers/summary/function_ids.py index 75c3e046a0..3694503d41 100644 --- a/slither/printers/summary/function_ids.py +++ b/slither/printers/summary/function_ids.py @@ -24,14 +24,19 @@ def output(self, _filename): all_tables = [] for contract in self.slither.contracts_derived: txt += '\n{}:\n'.format(contract.name) - table = MyPrettyTable(['Name', 'ID']) + table = MyPrettyTable(['Solidity Signature', 'ID', 'Return types']) for function in contract.functions: if function.visibility in ['public', 'external']: - table.add_row([function.solidity_signature, hex(get_function_id(function.solidity_signature))]) + _, _, returns = function.signature + table.add_row([function.solidity_signature, + hex(get_function_id(function.solidity_signature)), + ','.join(returns)]) for variable in contract.state_variables: if variable.visibility in ['public']: - sig = variable.function_name - table.add_row([sig, hex(get_function_id(sig))]) + _, _, returns = variable.signature + table.add_row([variable.solidity_signature, + hex(get_function_id(variable.solidity_signature)), + ','.join(returns)]) txt += str(table) + '\n' all_tables.append((contract.name, table)) diff --git a/slither/tools/erc_conformance/erc/ercs.py b/slither/tools/erc_conformance/erc/ercs.py index 5af0003571..de5c8517a2 100644 --- a/slither/tools/erc_conformance/erc/ercs.py +++ b/slither/tools/erc_conformance/erc/ercs.py @@ -46,7 +46,7 @@ def _check_signature(erc_function, contract, ret): ret["missing_function"].append(missing_func.data) return - function_return_type = [export_return_type_from_variable(state_variable_as_function)] + function_return_type = export_return_type_from_variable(state_variable_as_function) function_view = True else: diff --git a/slither/utils/type.py b/slither/utils/type.py index 6ede3e75ca..ee55079e2a 100644 --- a/slither/utils/type.py +++ b/slither/utils/type.py @@ -1,26 +1,53 @@ -from slither.core.solidity_types import (ArrayType, MappingType, ElementaryType) +import math +from typing import Union, List +from slither.core.solidity_types import (ArrayType, MappingType, ElementaryType, UserDefinedType, FunctionType) +from slither.core.solidity_types.type import Type +from slither.core.variables.variable import Variable -def _add_mapping_parameter(t, l): + +def _add_mapping_parameter(t: Type, l: List[Type]): while isinstance(t, MappingType): - l.append(t.type_from) + l.append(convert_type_for_solidity_signature(t.type_from)) t = t.type_to - _add_array_parameter(t, l) + + if isinstance(t, ArrayType): + _add_array_parameter(t, l) -def _add_array_parameter(t, l): +def _add_array_parameter(t: Type, l: List[Type]): while isinstance(t, ArrayType): - l.append(ElementaryType('uint256')) + l.append(ElementaryType("uint256")) t = t.type + if isinstance(t, MappingType): + _add_mapping_parameter(t, l) + -def export_nested_types_from_variable(variable): +def convert_type_for_solidity_signature(t: Type) -> Type: + from slither.core.declarations import Contract, Enum + if isinstance(t, UserDefinedType) and isinstance(t.type, Contract): + return ElementaryType("address") + if isinstance(t, UserDefinedType) and isinstance(t.type, Enum): + number_values = len(t.type.values) + # IF below 65536, avoid calling log2 + if number_values <= 256: + uint = "8" + elif number_values <= 65536: + uint = "16" + else: + uint = int(math.log2(number_values)) + return ElementaryType(f"uint{uint}") + return t + + +def export_nested_types_from_variable(variable: Variable) -> List[Type]: """ Export the list of nested types (mapping/array) :param variable: :return: list(Type) """ - l = [] + l: List[Type] = [] if isinstance(variable.type, MappingType): t = variable.type _add_mapping_parameter(t, l) @@ -32,24 +59,52 @@ def export_nested_types_from_variable(variable): return l -def export_return_type_from_variable(variable): +def export_return_type_from_variable(variable_or_type: Union[Type, Variable], all_types: bool = True) -> List[Type]: """ - Return the type returned by a variable - :param variable + Return the type returned by a variable. + If all_types set to false, filter array/mapping. This is useful as the mapping/array in a structure are not + returned by solidity + + :param variable_or_type + :param all_types :return: Type """ - if isinstance(variable, MappingType): - return export_return_type_from_variable(variable.type_to) - - if isinstance(variable, ArrayType): - return variable.type - - if isinstance(variable.type, MappingType): - return export_return_type_from_variable(variable.type.type_to) - - if isinstance(variable.type, ArrayType): - return variable.type.type - - return variable.type - - + from slither.core.declarations import Structure + + if isinstance(variable_or_type, Type): + if isinstance(variable_or_type, MappingType): + if not all_types: + return [] + return export_return_type_from_variable(variable_or_type.type_to) + + if isinstance(variable_or_type, ArrayType): + if not all_types: + return [] + return export_return_type_from_variable(variable_or_type.type) + + if isinstance(variable_or_type, UserDefinedType) and isinstance(variable_or_type.type, Structure): + ret = [] + for r in variable_or_type.type.elems_ordered: + ret += export_return_type_from_variable(r, all_types=False) + + return ret + + return [variable_or_type] + else: + if isinstance(variable_or_type.type, MappingType): + if not all_types: + return [] + return export_return_type_from_variable(variable_or_type.type.type_to) + + if isinstance(variable_or_type.type, ArrayType): + if not all_types: + return [] + return export_return_type_from_variable(variable_or_type.type.type) + + if isinstance(variable_or_type.type, UserDefinedType) and isinstance(variable_or_type.type.type, Structure): + ret = [] + for r in variable_or_type.type.type.elems_ordered: + ret += export_return_type_from_variable(r, all_types=False) + return ret + + return [variable_or_type.type]