Skip to content

Commit

Permalink
Merge pull request #431 from crytic/dev-echidna-printer
Browse files Browse the repository at this point in the history
Improvement in echidna printer
  • Loading branch information
montyly authored Apr 3, 2020
2 parents 8ba09c9 + 611db9e commit 6420a19
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 24 deletions.
9 changes: 9 additions & 0 deletions slither/core/declarations/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,15 @@ def is_fallback(self):
"""
return self._function_type == FunctionType.FALLBACK

@property
def is_receive(self):
"""
Determine if the function is the receive function for the contract
Returns
(bool)
"""
return self._function_type == FunctionType.RECEIVE

# endregion
###################################################################################
###################################################################################
Expand Down
68 changes: 44 additions & 24 deletions slither/printers/guidance/echidna.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,63 +3,86 @@

import json
from collections import defaultdict
from typing import Dict, List, Set, Tuple

from slither.core.cfg.node import Node
from slither.core.declarations import Function
from slither.core.slither_core import Slither
from slither.core.variables.variable import Variable
from slither.printers.abstract_printer import AbstractPrinter
from slither.core.declarations.solidity_variables import SolidityVariableComposed, SolidityFunction
from slither.core.declarations.solidity_variables import SolidityVariableComposed, SolidityFunction, SolidityVariable
from slither.slithir.operations import Member, Operation
from slither.slithir.operations.binary import Binary, BinaryType
from slither.core.variables.state_variable import StateVariable
from slither.slithir.variables import Constant


def _extract_payable(slither):
ret = {}
def _get_name(f: Function) -> str:
if f.is_fallback or f.is_receive:
return f'()'
return f.full_name


def _extract_payable(slither: Slither) -> Dict[str, List[str]]:
ret: Dict[str, List[str]] = {}
for contract in slither.contracts:
payable_functions = [f.full_name for f in contract.functions_entry_points if f.payable]
payable_functions = [_get_name(f) for f in contract.functions_entry_points if f.payable]
if payable_functions:
ret[contract.name] = payable_functions
return ret

def _extract_solidity_variable_usage(slither, sol_var):
ret = {}

def _extract_solidity_variable_usage(slither: Slither, sol_var: SolidityVariable) -> Dict[str, List[str]]:
ret: Dict[str, List[str]] = {}
for contract in slither.contracts:
functions_using_sol_var = []
for f in contract.functions_entry_points:
for v in f.all_solidity_variables_read():
if v == sol_var:
functions_using_sol_var.append(f.full_name)
functions_using_sol_var.append(_get_name(f))
break
if functions_using_sol_var:
ret[contract.name] = functions_using_sol_var
return ret

def _extract_constant_functions(slither):
ret = {}

def _extract_constant_functions(slither: Slither) -> Dict[str, List[str]]:
ret: Dict[str, List[str]] = {}
for contract in slither.contracts:
cst_functions = [f.full_name for f in contract.functions_entry_points if f.view or f.pure]
cst_functions = [_get_name(f) for f in contract.functions_entry_points if f.view or f.pure]
cst_functions += [v.function_name for v in contract.state_variables if v.visibility in ['public']]
if cst_functions:
ret[contract.name] = cst_functions
return ret

def _extract_assert(slither):
ret = {}

def _extract_assert(slither: Slither) -> Dict[str, List[str]]:
ret: Dict[str, List[str]] = {}
for contract in slither.contracts:
functions_using_assert = []
for f in contract.functions_entry_points:
for v in f.all_solidity_calls():
if v == SolidityFunction('assert(bool)'):
functions_using_assert.append(f.full_name)
functions_using_assert.append(_get_name(f))
break
if functions_using_assert:
ret[contract.name] = functions_using_assert
return ret

def _extract_constants_from_irs(irs, all_cst_used, all_cst_used_in_binary, context_explored):

def _extract_constants_from_irs(irs: List[Operation],
all_cst_used: List,
all_cst_used_in_binary: Dict,
context_explored: Set[Node]):
for ir in irs:
if isinstance(ir, Binary):
for r in ir.read:
if isinstance(r, Constant):
all_cst_used_in_binary[BinaryType.str(ir.type)].append(r.value)
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(r.value)
if isinstance(r, StateVariable):
Expand All @@ -74,9 +97,10 @@ def _extract_constants_from_irs(irs, all_cst_used, all_cst_used_in_binary, conte
all_cst_used_in_binary,
context_explored)

def _extract_constants(slither):
ret_cst_used = defaultdict(dict)
ret_cst_used_in_binary = defaultdict(dict)

def _extract_constants(slither: Slither) -> Tuple[Dict[str, Dict[str, List]], Dict[str, Dict[str, Dict]]]:
ret_cst_used: Dict[str, Dict[str, List]] = defaultdict(dict)
ret_cst_used_in_binary: Dict[str, Dict[str, Dict]] = defaultdict(dict)
for contract in slither.contracts:
for function in contract.functions_entry_points:
all_cst_used = []
Expand All @@ -93,9 +117,7 @@ def _extract_constants(slither):
ret_cst_used[contract.name][function.full_name] = all_cst_used
if all_cst_used_in_binary:
ret_cst_used_in_binary[contract.name][function.full_name] = all_cst_used_in_binary
return (ret_cst_used, ret_cst_used_in_binary)


return ret_cst_used, ret_cst_used_in_binary


class Echidna(AbstractPrinter):
Expand All @@ -104,7 +126,6 @@ class Echidna(AbstractPrinter):

WIKI = 'https://github.com/trailofbits/slither/wiki/Printer-documentation#echidna'


def output(self, filename):
"""
Output the inheritance relation
Expand All @@ -120,14 +141,13 @@ def output(self, filename):
block_number = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('block.number'))
msg_sender = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('msg.sender'))
SolidityVariableComposed('msg.sender'))
msg_gas = _extract_solidity_variable_usage(self.slither,
SolidityVariableComposed('msg.gas'))
assert_usage = _extract_assert(self.slither)
cst_functions = _extract_constant_functions(self.slither)
(cst_used, cst_used_in_binary) = _extract_constants(self.slither)


d = {'payable': payable,
'timestamp': timestamp,
'block_number': block_number,
Expand All @@ -142,4 +162,4 @@ def output(self, filename):

res = self.generate_output(json.dumps(d, indent=4))

return res
return res

0 comments on commit 6420a19

Please sign in to comment.