Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Fix incorrect solidity function signature #483

Closed
wants to merge 1 commit into from
Closed
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
10 changes: 2 additions & 8 deletions slither/core/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -726,21 +727,14 @@ 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:
"""
Return a signature following the Solidity Standard
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) + ')'


Expand Down
29 changes: 2 additions & 27 deletions slither/core/variables/state_variable.py
Original file line number Diff line number Diff line change
@@ -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):

Expand All @@ -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
Expand Down
49 changes: 31 additions & 18 deletions slither/core/variables/variable.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion slither/printers/guidance/echidna.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions slither/printers/summary/function_ids.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))

Expand Down
2 changes: 1 addition & 1 deletion slither/tools/erc_conformance/erc/ercs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
107 changes: 81 additions & 26 deletions slither/utils/type.py
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we replace this with export_return_type_from_variable(variable_or_type.type)?

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]