import logging
from copy import copy
from fdg.utils import str_without_space_line
from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.laser.smt.expression import simplify_yes
log = logging.getLogger(__name__)
[docs]
class InstructionCoverage():
def __init__(self,functions:list):
self.coverage={}
self.contract_coverage=0
self.function_instruction_indices={ftn:[] for ftn in functions}
self.function_instruction_complete_status={ftn:False for ftn in functions}
self.function_special = [] # not completely going through
[docs]
def update_coverage(self,global_state:GlobalState,opcode:str):
# Record coverage
code = global_state.environment.code.bytecode
if code not in self.coverage.keys():
number_of_instructions = len(
global_state.environment.code.instruction_list
)
self.coverage[code] = (
number_of_instructions,
[False] * number_of_instructions,
)
self.coverage[code][1][global_state.mstate.pc] = True
# collection instructions for each function
self.collect_function_instructions(global_state)
# # compute coverage
# self.compute_coverage()
#
# print(f'{global_state.environment.active_function_name}: {global_state.mstate.pc}: {global_state.instruction}')
# if global_state.instruction['address']in [120,124,128]:
# print(f'xx')
# function=global_state.environment.active_function_name
# if function in ['use3(uint32)']:
# print(f'{function}: {global_state.mstate.pc}: {global_state.instruction}')
#
# self.print_state_info(global_state,opcode,None,'TransferERC20Token(address,uint256)')
[docs]
def collect_function_instructions(self,global_state:GlobalState):
# collection instructions for each function
function = global_state.environment.active_function_name
instruction=global_state.instruction
if function.__eq__('constructor'): return
if function not in self.function_instruction_indices.keys():
self.function_instruction_indices[function]=[]
pc = global_state.mstate.pc
if pc not in self.function_instruction_indices[function]:
self.function_instruction_indices[function].append(pc)
if str(instruction['opcode']) in ['STOP','RETURN']:
self.function_instruction_complete_status[function]=True
[docs]
def call_at_end_of_preprocessing(self):
# print(f'\nfunction instruction collection status:')
# for ftn,v in self.function_instruction_complete_status.items():
# if not v:
# self.function_special.append(ftn)
# print(f'\t{ftn}:{v}')
# print(f'the number of instructions of functions')
# for k, v in self.function_instruction_indices.items():
# print(f'\t{k}:{len(v)}')
return self.compute_coverage()
[docs]
def compute_coverage(self):
for code, code_cov in self.coverage.items():
self.contract_coverage = sum(code_cov[1]) / float(code_cov[0]) * 100
print("preprocessing: Achieved {:.2f}% coverage.".format(self.contract_coverage))
return self.contract_coverage
[docs]
def print_state_info(self,global_state:GlobalState,opcode:str,given_opcode:str=None,target_function:str=None):
def print_based_on_opcode(opcode:str):
# print(f'{function}: {global_state.mstate.pc}: {global_state.environment.code.instruction_list}')
if opcode=='JUMPI':
print(f'{function}: {global_state.mstate.pc}: {global_state.instruction}')
condition = global_state.mstate.stack[-2]
print(f'sim_condi:{str_without_space_line(simplify_yes(copy(condition)))}')
print(f' conditi:{str_without_space_line(condition)}')
# condi = str(condition).translate({ord(term): None for term in string.whitespace})
# print(f'condition:{condi}')
# if 'Store' not in condi:
# return
# p = NestedParser()
# results = p.parse(condi)
# if len(results) > 0:
# print(f'extracted info: {results}')
else:
print(f'{function}: {global_state.mstate.pc}: {global_state.instruction}')
function = global_state.environment.active_function_name
if target_function is not None:
if not function.__eq__(target_function):
return
if given_opcode is not None:
if not opcode.__eq__(given_opcode):
return
print_based_on_opcode(opcode)
return
print_based_on_opcode(opcode)