From e881b855fb938c4a1c618e645a2f0e78940703c4 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Mon, 4 Mar 2024 02:49:43 +0000 Subject: [PATCH] Add new features --- mythril/analysis/symbolic.py | 2 + mythril/concolic/concolic_execution.py | 1 - mythril/concolic/find_trace.py | 10 +- mythril/interfaces/cli.py | 17 +- mythril/laser/plugin/plugins/__init__.py | 2 + .../plugins/coverage_metrics/__init__.py | 1 + .../plugins/coverage_metrics/constants.py | 1 + .../plugins/coverage_metrics/coverage_data.py | 73 ++++++ .../coverage_metrics/metrics_plugin.py | 131 ++++++++++ mythril/laser/plugin/plugins/trace.py | 48 ++++ tests/concolic/concolic_tests.py | 240 ++++++++++++++++++ .../coverage_metrics_test.py | 19 ++ .../concolic_io/multi_contract_example.sol | 54 ++++ .../multi_contract_example_input.json | 108 ++++++++ .../testdata/concolic_io/multiple_example.sol | 25 ++ .../concolic_io/multiple_example_input.json | 48 ++++ tests/testdata/concolic_io/simple_example.sol | 19 ++ .../concolic_io/simple_example_input.json | 48 ++++ tests/testdata/concolic_io/two_contract.sol | 42 +++ .../concolic_io/two_contract_input.json | 78 ++++++ tests/testdata/inputs/coverage.sol.o | 1 + 21 files changed, 953 insertions(+), 15 deletions(-) create mode 100644 mythril/laser/plugin/plugins/coverage_metrics/__init__.py create mode 100644 mythril/laser/plugin/plugins/coverage_metrics/constants.py create mode 100644 mythril/laser/plugin/plugins/coverage_metrics/coverage_data.py create mode 100644 mythril/laser/plugin/plugins/coverage_metrics/metrics_plugin.py create mode 100644 mythril/laser/plugin/plugins/trace.py create mode 100644 tests/concolic/concolic_tests.py create mode 100644 tests/integration_tests/coverage_metrics_test.py create mode 100644 tests/testdata/concolic_io/multi_contract_example.sol create mode 100644 tests/testdata/concolic_io/multi_contract_example_input.json create mode 100644 tests/testdata/concolic_io/multiple_example.sol create mode 100644 tests/testdata/concolic_io/multiple_example_input.json create mode 100644 tests/testdata/concolic_io/simple_example.sol create mode 100644 tests/testdata/concolic_io/simple_example_input.json create mode 100644 tests/testdata/concolic_io/two_contract.sol create mode 100644 tests/testdata/concolic_io/two_contract_input.json create mode 100644 tests/testdata/inputs/coverage.sol.o diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py index 6b41c1a6..a1de5476 100644 --- a/mythril/analysis/symbolic.py +++ b/mythril/analysis/symbolic.py @@ -24,6 +24,7 @@ from mythril.laser.plugin.plugins import ( MutationPrunerBuilder, DependencyPrunerBuilder, CoveragePluginBuilder, + CoverageMetricsPluginBuilder, CallDepthLimitBuilder, InstructionProfilerBuilder, SymbolicSummaryPluginBuilder, @@ -143,6 +144,7 @@ class SymExecWrapper: ) plugin_loader = LaserPluginLoader() + plugin_loader.load(CoverageMetricsPluginBuilder()) if not args.disable_coverage_strategy: plugin_loader.load(CoveragePluginBuilder()) if not args.disable_mutation_pruner: diff --git a/mythril/concolic/concolic_execution.py b/mythril/concolic/concolic_execution.py index 6d44d004..4dfed873 100644 --- a/mythril/concolic/concolic_execution.py +++ b/mythril/concolic/concolic_execution.py @@ -74,7 +74,6 @@ def concolic_execution( :param solver_timeout: Solver timeout """ - init_state, trace = concrete_execution(concrete_data) args.solver_timeout = solver_timeout output_list = flip_branches( diff --git a/mythril/concolic/find_trace.py b/mythril/concolic/find_trace.py index 5700f4be..5e32c8bc 100644 --- a/mythril/concolic/find_trace.py +++ b/mythril/concolic/find_trace.py @@ -12,11 +12,13 @@ from mythril.laser.ethereum.svm import LaserEVM from mythril.laser.ethereum.state.world_state import WorldState from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.time_handler import time_handler +from mythril.laser.plugin.plugins import TraceFinderBuilder from mythril.laser.ethereum.transaction.concolic import execute_transaction from mythril.laser.plugin.loader import LaserPluginLoader from mythril.laser.smt import Expression, BitVec, symbol_factory from mythril.laser.ethereum.transaction.transaction_models import tx_id_manager from mythril.plugin.discovery import PluginDiscovery +from mythril.support.support_args import args def setup_concrete_initial_state(concrete_data: ConcreteData) -> WorldState: @@ -30,12 +32,11 @@ def setup_concrete_initial_state(concrete_data: ConcreteData) -> WorldState: account = Account(address, concrete_storage=True) account.code = Disassembly(details["code"][2:]) account.nonce = details["nonce"] - if isinstance(type(details["storage"]), str): + if isinstance(details["storage"], str): details["storage"] = eval(details["storage"]) # type: ignore for key, value in details["storage"].items(): key_bitvec = symbol_factory.BitVecVal(int(key, 16), 256) account.storage[key_bitvec] = symbol_factory.BitVecVal(int(value, 16), 256) - world_state.put_account(account) account.set_balance(int(details["balance"], 16)) return world_state @@ -47,16 +48,15 @@ def concrete_execution(concrete_data: ConcreteData) -> Tuple[WorldState, List]: :param concrete_data: Concrete data :return: path trace """ + args.pruning_factor = 1 tx_id_manager.restart_counter() init_state = setup_concrete_initial_state(concrete_data) laser_evm = LaserEVM(execution_timeout=1000) laser_evm.open_states = [deepcopy(init_state)] plugin_loader = LaserPluginLoader() - assert PluginDiscovery().is_installed("myth_concolic_execution") - trace_plugin = PluginDiscovery().installed_plugins["myth_concolic_execution"]() + plugin_loader.load(TraceFinderBuilder()) time_handler.start_execution(laser_evm.execution_timeout) - plugin_loader.load(trace_plugin) laser_evm.time = datetime.now() plugin_loader.instrument_virtual_machine(laser_evm, None) for transaction in concrete_data["steps"]: diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py index 40d503b8..0e462cff 100644 --- a/mythril/interfaces/cli.py +++ b/mythril/interfaces/cli.py @@ -300,15 +300,14 @@ def main() -> None: ) create_disassemble_parser(disassemble_parser) - if PluginDiscovery().is_installed("myth_concolic_execution"): - concolic_parser = subparsers.add_parser( - CONCOLIC_LIST[0], - help="Runs concolic execution to flip the desired branches", - aliases=CONCOLIC_LIST[1:], - parents=[], - formatter_class=RawTextHelpFormatter, - ) - create_concolic_parser(concolic_parser) + concolic_parser = subparsers.add_parser( + CONCOLIC_LIST[0], + help="Runs concolic execution to flip the desired branches", + aliases=CONCOLIC_LIST[1:], + parents=[], + formatter_class=RawTextHelpFormatter, + ) + create_concolic_parser(concolic_parser) foundry_parser = subparsers.add_parser( FOUNDRY_LIST[0], diff --git a/mythril/laser/plugin/plugins/__init__.py b/mythril/laser/plugin/plugins/__init__.py index 11ad99d7..190ac298 100644 --- a/mythril/laser/plugin/plugins/__init__.py +++ b/mythril/laser/plugin/plugins/__init__.py @@ -12,3 +12,5 @@ from mythril.laser.plugin.plugins.mutation_pruner import MutationPrunerBuilder from mythril.laser.plugin.plugins.call_depth_limiter import CallDepthLimitBuilder from mythril.laser.plugin.plugins.instruction_profiler import InstructionProfilerBuilder from mythril.laser.plugin.plugins.summary import SymbolicSummaryPluginBuilder +from mythril.laser.plugin.plugins.trace import TraceFinderBuilder +from mythril.laser.plugin.plugins.coverage_metrics import CoverageMetricsPluginBuilder diff --git a/mythril/laser/plugin/plugins/coverage_metrics/__init__.py b/mythril/laser/plugin/plugins/coverage_metrics/__init__.py new file mode 100644 index 00000000..5c9c8f32 --- /dev/null +++ b/mythril/laser/plugin/plugins/coverage_metrics/__init__.py @@ -0,0 +1 @@ +from .metrics_plugin import CoverageMetricsPluginBuilder diff --git a/mythril/laser/plugin/plugins/coverage_metrics/constants.py b/mythril/laser/plugin/plugins/coverage_metrics/constants.py new file mode 100644 index 00000000..e7df9cf7 --- /dev/null +++ b/mythril/laser/plugin/plugins/coverage_metrics/constants.py @@ -0,0 +1 @@ +BATCH_OF_STATES = 5 diff --git a/mythril/laser/plugin/plugins/coverage_metrics/coverage_data.py b/mythril/laser/plugin/plugins/coverage_metrics/coverage_data.py new file mode 100644 index 00000000..491c141f --- /dev/null +++ b/mythril/laser/plugin/plugins/coverage_metrics/coverage_data.py @@ -0,0 +1,73 @@ +import json +from typing import Dict, List + +from mythril.support.support_utils import get_code_hash +from mythril.laser.execution_info import ExecutionInfo + + +class InstructionCoverageInfo(ExecutionInfo): + def __init__(self): + self._instruction_coverage = {} # type: Dict[str, int] + + def as_dict(self): + return dict(instruction_discovery_time=self._instruction_coverage) + + def get_code_instr_hex(self, code: str, instruction: int): + code_hash = get_code_hash(code)[2:] + instruction_hex = hex(instruction)[2:] + return "{}:{}".format(code_hash, instruction_hex) + + def is_covered(self, code: str, instruction: int): + code_instr_hex = self.get_code_instr_hex(code, instruction) + return code_instr_hex in self._instruction_coverage + + def add_data(self, code: str, instruction: int, discovery_time: int): + code_instr_hex = self.get_code_instr_hex(code, instruction) + self._instruction_coverage[code_instr_hex] = discovery_time + + def output(self, filename: str): + with open(filename, "w") as outfile: + json.dump( + self._instruction_coverage, default=lambda o: o.__dict__, fp=outfile + ) + + +class CoverageData: + def __init__( + self, + instructions_covered: int, + total_instructions: int, + branches_covered: int, + tx_id: int, + total_branches: int, + state_counter: int, + code: str, + time_elapsed: int, + ): + self.instructions_covered = instructions_covered + self.total_instructions = total_instructions + self.branches_covered = branches_covered + self.tx_id = tx_id + self.total_branches = total_branches + self.state_counter = state_counter + self.code_hash = get_code_hash(code)[2:] + self.time_elapsed = time_elapsed + + def as_dict(self): + return self.__dict__ + + +class CoverageTimeSeries(ExecutionInfo): + def __init__(self): + self.coverage = [] # type: List[CoverageData] + + def output(self, filename: str): + with open(filename, "w") as outfile: + json.dump(self.coverage, default=lambda o: o.__dict__, fp=outfile) + + def as_dict(self): + return dict(coverage=self.coverage) + + def add_data(self, *args, **kwargs): + cov_data = CoverageData(*args, **kwargs) + self.coverage.append(cov_data.as_dict()) diff --git a/mythril/laser/plugin/plugins/coverage_metrics/metrics_plugin.py b/mythril/laser/plugin/plugins/coverage_metrics/metrics_plugin.py new file mode 100644 index 00000000..7b4bdbf0 --- /dev/null +++ b/mythril/laser/plugin/plugins/coverage_metrics/metrics_plugin.py @@ -0,0 +1,131 @@ +from mythril.laser.ethereum.svm import LaserEVM +from mythril.laser.plugin.interface import LaserPlugin +from mythril.laser.plugin.builder import PluginBuilder +from mythril.laser.ethereum.state.global_state import GlobalState +from .coverage_data import ( + CoverageTimeSeries, + InstructionCoverageInfo, +) +from .constants import BATCH_OF_STATES +from typing import Dict, Tuple, List + +import time +import logging + +log = logging.getLogger(__name__) + + +class CoverageMetricsPluginBuilder(PluginBuilder): + """CoveragePlugin + Checks Instruction and branch coverage and puts it to data.json file + which appears in the directory in which mythril is run. + """ + + plugin_default_enabled = True + enabled = True + + author = "MythX Development Team" + plugin_name = "MythX Coverage Metrics" + plugin_license = "All rights reserved." + plugin_type = "Laser Plugin" + plugin_description = ( + "This plugin measures coverage throughout symbolic execution," + " reporting it at the end in the MythX coverage format." + ) + + def __call__(self, *args, **kwargs): + """Constructs the plugin""" + return LaserCoveragePlugin() + + +class LaserCoveragePlugin(LaserPlugin): + def __init__(self): + self.instruction_coverage_data = {} # type: Dict[str, Tuple[int, Dict[bool]]] + self.branch_possibilities = {} # type: Dict[str, Dict[int, List]] + self.tx_id = 0 + self.state_counter = 0 + self.coverage = CoverageTimeSeries() + self.instruction_coverage_info = InstructionCoverageInfo() + self.start_time = time.time_ns() + + def initialize(self, symbolic_vm: LaserEVM) -> None: + """Initializes the instruction coverage plugin + + Introduces hooks for each instruction + :param symbolic_vm: The symbolic virtual machine to initialise this plugin for + """ + log.info("Initializing coverage metrics plugin") + + self.instruction_coverage_data = {} + self.branch_possibilities = {} + self.tx_id = 0 + + # Add the instruction coverage ExecutionInfo to laser vm for use in reporting + symbolic_vm.execution_info.append(self.instruction_coverage_info) + symbolic_vm.execution_info.append(self.coverage) + + @symbolic_vm.laser_hook("execute_state") + def execute_state_hook(global_state: GlobalState): + self._update_instruction_coverage_data(global_state) + self._update_branch_coverage_data(global_state) + self.state_counter += 1 + if self.state_counter == BATCH_OF_STATES: + self._record_coverage() + self.state_counter = 0 + + @symbolic_vm.laser_hook("stop_sym_trans") + def execute_stop_sym_trans_hook(): + self.tx_id += 1 + + # The following is useful for debugging + # @symbolic_vm.laser_hook("stop_sym_exec") + # def execute_stop_sym_exec_hook(): + # self.coverage.output("coverage_data.json") + # self.instruction_coverage_info.output("instruction_discovery_data.json") + + def _update_instruction_coverage_data(self, global_state: GlobalState): + """Records instruction coverage""" + code = global_state.environment.code.bytecode + if code not in self.instruction_coverage_data.keys(): + number_of_instructions = len(global_state.environment.code.instruction_list) + self.instruction_coverage_data[code] = (number_of_instructions, {}) + current_instr = global_state.get_current_instruction()["address"] + if self.instruction_coverage_info.is_covered(code, current_instr) is False: + self.instruction_coverage_info.add_data( + code, current_instr, time.time_ns() - self.start_time + ) + self.instruction_coverage_data[code][1][current_instr] = True + + def _update_branch_coverage_data(self, global_state: GlobalState): + """Records branch coverage""" + code = global_state.environment.code.bytecode + if code not in self.branch_possibilities: + self.branch_possibilities[code] = {} + + if global_state.get_current_instruction()["opcode"] != "JUMPI": + return + addr = global_state.get_current_instruction()["address"] + jump_addr = global_state.mstate.stack[-1] + if jump_addr.symbolic: + log.debug("Encountered a symbolic jump, ignoring it for branch coverage") + return + self.branch_possibilities[code][addr] = [addr + 1, jump_addr.value] + + def _record_coverage(self): + for code, code_cov in self.instruction_coverage_data.items(): + total_branches = 0 + branches_covered = 0 + for jumps, branches in self.branch_possibilities[code].items(): + for branch in branches: + total_branches += 1 + branches_covered += branch in code_cov[1] + self.coverage.add_data( + code=code, + instructions_covered=len(code_cov[1]), + total_instructions=code_cov[0], + branches_covered=branches_covered, + tx_id=self.tx_id, + total_branches=total_branches, + state_counter=self.state_counter, + time_elapsed=time.time_ns() - self.start_time, + ) diff --git a/mythril/laser/plugin/plugins/trace.py b/mythril/laser/plugin/plugins/trace.py new file mode 100644 index 00000000..cbb2ba32 --- /dev/null +++ b/mythril/laser/plugin/plugins/trace.py @@ -0,0 +1,48 @@ +from mythril.laser.plugin.interface import LaserPlugin +from mythril.laser.plugin.builder import PluginBuilder +from mythril.laser.ethereum.state.global_state import GlobalState +from mythril.laser.ethereum.svm import LaserEVM +from typing import List, Tuple + + +class TraceFinderBuilder(PluginBuilder): + name = "trace-finder" + plugin_default_enabled = True + enabled = True + + author = "MythX Development Team" + name = "MythX Trace Finder" + plugin_license = "All rights reserved." + plugin_type = "Laser Plugin" + plugin_version = "0.0.1 " + plugin_description = "This plugin merges states after the end of a transaction" + + def __call__(self, *args, **kwargs): + return TraceFinder() + + +class TraceFinder(LaserPlugin): + def __init__(self): + self._reset() + + def _reset(self): + self.tx_trace: List[List[Tuple[int, str]]] = [] + + def initialize(self, symbolic_vm: LaserEVM): + """Initializes Trace Finder + + Introduces hooks during the start of the execution and each execution state + :param symbolic_vm: + :return: + """ + self._reset() + + @symbolic_vm.laser_hook("start_exec") + def start_sym_trans_hook(): + self.tx_trace.append([]) + + @symbolic_vm.laser_hook("execute_state") + def trace_jumpi_hook(global_state: GlobalState): + self.tx_trace[-1].append( + (global_state.mstate.pc, global_state.current_transaction.id) + ) diff --git a/tests/concolic/concolic_tests.py b/tests/concolic/concolic_tests.py new file mode 100644 index 00000000..230816aa --- /dev/null +++ b/tests/concolic/concolic_tests.py @@ -0,0 +1,240 @@ +import binascii +import json +import pathlib +import pytest +import subprocess + +from copy import deepcopy +from datetime import datetime +from mock import patch +from subprocess import check_output, CalledProcessError +from tests import BaseTestCase, PROJECT_DIR, TESTDATA + +from mythril.concolic import concrete_execution +from mythril.concolic.find_trace import setup_concrete_initial_state +from mythril.disassembler.asm import disassemble +from mythril.concolic.concrete_data import ConcreteData +from mythril.laser.ethereum import util + +from mythril.disassembler.disassembly import Disassembly +from mythril.laser.ethereum.svm import LaserEVM +from mythril.laser.ethereum.state.world_state import WorldState +from mythril.laser.ethereum.state.account import Account +from mythril.laser.ethereum.time_handler import time_handler +from mythril.laser.ethereum.transaction.concolic import execute_transaction +from mythril.laser.plugin.loader import LaserPluginLoader +from mythril.laser.smt import Expression, BitVec, symbol_factory +from mythril.laser.plugin.plugins import TraceFinderBuilder + +MYTH = str(PROJECT_DIR / "myth") + + +def output_of(command): + try: + return json.loads(check_output(command, shell=True).decode("UTF-8")) + except CalledProcessError as exc: + return json.loads(exc.output.decode("UTF-8")) + + +# TODO: Try using some python EVM for these tests + + +def validate_simple_example(output, branches): + for branch_output, branch in zip(output, branches): + if branch == "153": + # Validation for initialState + + # Validation for tx steps + tx_step = branch_output["steps"][1] + assert tx_step["input"] == tx_step["calldata"] + assert int(tx_step["input"][10:], 16) == 3 + + +def validate_multiple_example(output, branches): + for branch_output, branch in zip(output, branches): + if branch == "153": + # Validation for initialState + + # Validation for tx steps + tx_step = branch_output["steps"][1] + assert tx_step["input"] == tx_step["calldata"] + assert int(tx_step["input"][10:], 16) == 3 + elif branch == "192": + # Validation for initialState + + # Validation for tx steps + tx_step = branch_output["steps"][1] + assert tx_step["input"] == tx_step["calldata"] + assert int(tx_step["input"][10:], 16) == 5 + elif branch == "243": + # Validation for initialState + + # Validation for tx steps + tx_step = branch_output["steps"][1] + assert tx_step["input"] == tx_step["calldata"] + assert int(tx_step["input"][10:], 16) == 7 + + +def validate_two_contract(output, branches): + for branch_output, branch in zip(output, branches): + if branch == "311": + + # Validation for initialState + # Validation for tx steps + assert ( + int(branch_output["steps"][1]["input"][10:], 16) + + int(branch_output["steps"][3]["input"][10:], 16) + == 11 + ) + + if branch == "341": + # Validation for initialState + + # Validation for tx steps + assert ( + int(branch_output["steps"][1]["input"][10:], 16) + + int(branch_output["steps"][3]["input"][10:], 16) + == 30 + ) + + if branch == "371": + # Validation for initialState + + # Validation for tx steps + assert ( + int(branch_output["steps"][1]["input"][10:], 16) + + int(branch_output["steps"][3]["input"][10:], 16) + == 20 + ) + + +def validate_multi_contract(output, branches): + for branch_output, branch in zip(output, branches): + if branch == "453": + # Validation for initialState + + # Validation for tx steps + assert ( + int(branch_output["steps"][1]["input"][10:], 16) + + int(branch_output["steps"][3]["input"][10:], 16) + + int(branch_output["steps"][5]["input"][10:], 16) + == 10 + ) + + if branch == "483": + # Validation for initialState + + # Validation for tx steps + assert ( + int(branch_output["steps"][1]["input"][10:], 16) + + int(branch_output["steps"][3]["input"][10:], 16) + + int(branch_output["steps"][5]["input"][10:], 16) + == 25 + ) + + +validate_test_data = ( + ("simple_example_input.json", validate_simple_example, "153"), + ("multiple_example_input.json", validate_multiple_example, "153,192,243"), + ("two_contract_input.json", validate_two_contract, "311,341,371"), + ("multi_contract_example_input.json", validate_multi_contract, "453,483"), +) +check_state_validity_test_data = ( + ("simple_example_input.json", "153"), + ("multiple_example_input.json", "153,192,243"), + ("two_contract_input.json", "311,341,371"), + ("multi_contract_example_input.json", "453,483"), +) + +test_data_error = (("simple_example_input.json", "508"),) + + +@pytest.mark.parametrize("input_file,validate_function,branches", validate_test_data) +def test_concolic_conditions(input_file, validate_function, branches): + input_path = str(TESTDATA / "concolic_io" / input_file) + + command = f"{MYTH} concolic {input_path} --branches {branches}" + received_output = output_of(command) + branches = [branch for branch in branches.split(",")] + validate_function(received_output, branches) + + +@pytest.mark.parametrize("input_file,branch", test_data_error) +def test_concolic_error(input_file, branch): + input_path = str(TESTDATA / "concolic_io" / input_file) + command = f"{MYTH} concolic {input_path} --branches {branch}" + received_output = subprocess.run( + command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE + ) + + assert ( + f"The branch {branch} does not lead to a jump address, skipping this branch" + in received_output.stderr.decode("UTF-8") + ) + + +def get_pc_from_disassembler(concrete_data, branches): + init_state = setup_concrete_initial_state(concrete_data) + laser_evm = LaserEVM(execution_timeout=100) + + laser_evm.open_states = [deepcopy(init_state)] + plugin_loader = LaserPluginLoader() + trace_plugin = TraceFinderBuilder() + plugin_loader.load(trace_plugin) + laser_evm.time = datetime.now() + plugin_loader.instrument_virtual_machine(laser_evm, None) + + for transaction in concrete_data["steps"][:-1]: + execute_transaction( + laser_evm, + callee_address=transaction["address"], + caller_address=symbol_factory.BitVecVal( + int(transaction["origin"], 16), 256 + ), + origin_address=symbol_factory.BitVecVal( + int(transaction["origin"], 16), 256 + ), + gas_limit=int(transaction.get("gasLimit", "0x9999999999999999999999"), 16), + data=binascii.a2b_hex(transaction["input"][2:]), + gas_price=int(transaction.get("gasPrice", "0x773594000"), 16), + value=int(transaction["value"], 16), + track_gas=False, + ) + contract_addr = concrete_data["steps"][-1]["address"] + assert len(laser_evm.open_states) == 1 + instruction_list = ( + laser_evm.open_states[0].accounts[int(contract_addr, 16)].code.instruction_list + ) + branches = [ + util.get_instruction_index(instruction_list, branch) for branch in branches + ] + return branches + + +def run_concolic(input_path, output, branches): + with open(input_path) as f: + concrete_data = json.load(f) + _, input_trace = concrete_execution(concrete_data) + input_last_tx = input_trace[-1] + branches = [int(branch) for branch in branches.split(",")] + time_handler.start_execution(1000) + branches = get_pc_from_disassembler(concrete_data, branches) + for out, branch in zip(output, branches): + _, trace = concrete_execution(out) + last_tx = trace[-1] + tx_id = last_tx[0][1] + branch_idx = last_tx.index((branch, tx_id)) + input_idx = input_last_tx.index((branch, tx_id)) + + assert (branch_idx == input_idx) and last_tx[branch_idx + 1][ + 0 + ] != input_last_tx[branch_idx + 1][0] + + +@pytest.mark.parametrize("input_file,branches", check_state_validity_test_data) +def test_validate_concolic_output(input_file, branches): + input_path = str(TESTDATA / "concolic_io" / input_file) + + command = f"{MYTH} concolic {input_path} --branches {branches}" + received_output = output_of(command) + run_concolic(input_path, received_output, branches) diff --git a/tests/integration_tests/coverage_metrics_test.py b/tests/integration_tests/coverage_metrics_test.py new file mode 100644 index 00000000..b6cdf258 --- /dev/null +++ b/tests/integration_tests/coverage_metrics_test.py @@ -0,0 +1,19 @@ +import pytest + +from tests import PROJECT_DIR, TESTDATA +from utils import output_of + +MYTH = str(PROJECT_DIR / "myth") + + +test_data = [ + (open(f"{TESTDATA}/inputs/coverage.sol.o").read(), True), +] + + +@pytest.mark.parametrize("code, exists", test_data) +def test_basic_coverage(code, exists): + assert ( + "instruction_discovery_time" + in output_of(f"{MYTH} a -c 0x{code} --solver-timeout 1000 -o jsonv2") + ) == exists diff --git a/tests/testdata/concolic_io/multi_contract_example.sol b/tests/testdata/concolic_io/multi_contract_example.sol new file mode 100644 index 00000000..ff377330 --- /dev/null +++ b/tests/testdata/concolic_io/multi_contract_example.sol @@ -0,0 +1,54 @@ +pragma solidity 0.8.6; + +/** + * @title Storage + * @dev Store & retreive value in a variable + */ +contract D1 { + + uint256 number; + + function store(uint256 num) public { + number =num; + } + + function retval() public returns(uint256){ + return number; + } +} +contract D2 { + + + uint256 number; + + function store(uint256 num) external { + number = num; + } + function retval() public returns(uint256){ + return number; + } + +} + + +contract D3 { + D2 d2; + D1 d1; + constructor() public + { + d1 = D1(0x0901d12ebE1b195E5AA8748E62Bd7734aE19B51F); + d2 = D2(0x384f682f4a5AbefC8795Cc38a340dE9446dFAE7A); + } + function test(uint256 num) public returns(uint256) { + uint256 sum = d1.retval() + d2.retval() + num; + if (sum == 10) { + return sum + 10; + } + else if(sum == 25) { + return sum * 2; + } + else return sum*10; + return sum; + } +} + diff --git a/tests/testdata/concolic_io/multi_contract_example_input.json b/tests/testdata/concolic_io/multi_contract_example_input.json new file mode 100644 index 00000000..e8e1d129 --- /dev/null +++ b/tests/testdata/concolic_io/multi_contract_example_input.json @@ -0,0 +1,108 @@ +{ + "initialState": { + "accounts": { + "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe": { + "balance": "0x1000000", + "code": "", + "nonce": 0, + "storage": {} + }, + "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef": { + "balance": "0x0", + "code": "", + "nonce": 0, + "storage": {} + } + } + }, + "steps": [{ + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636057361d1461003b5780639c88345314610057575b600080fd5b6100556004803603810190610050919061009d565b610075565b005b61005f61007f565b60405161006c91906100d9565b60405180910390f35b8060008190555050565b60008054905090565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea2646970667358221220773ac8f6769c8b896b1ac993a29718d8f1463998522b37ec8e410ded6f1bd3d464736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0x0901d12ebE1b195E5AA8748E62Bd7734aE19B51F", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x6057361d0000000000000000000000000000000000000000000000000000000000000001", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636057361d1461003b5780639c88345314610057575b600080fd5b6100556004803603810190610050919061009d565b610075565b005b61005f61007f565b60405161006c91906100d9565b60405180910390f35b8060008190555050565b60008054905090565b60008135905061009781610103565b92915050565b6000602082840312156100b3576100b26100fe565b5b60006100c184828501610088565b91505092915050565b6100d3816100f4565b82525050565b60006020820190506100ee60008301846100ca565b92915050565b6000819050919050565b600080fd5b61010c816100f4565b811461011757600080fd5b5056fea264697066735822122046b7cc08908c1d17c2b2b523dfb5fc5a50e56068ce85f705f6f75ff54c7b16c864736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0x384f682f4a5AbefC8795Cc38a340dE9446dFAE7A", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x6057361d0000000000000000000000000000000000000000000000000000000000000001", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x608060405234801561001057600080fd5b50730901d12ebe1b195e5aa8748e62bd7734ae19b51f600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555073384f682f4a5abefc8795cc38a340de9446dfae7a6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506103f7806100c96000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806329e99f0714610030575b600080fd5b61004a60048036038101906100459190610238565b610060565b60405161005791906102a1565b60405180910390f35b6000808260008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16639c8834536040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156100cc57600080fd5b505af11580156100e0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101049190610265565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16639c8834536040518163ffffffff1660e01b8152600401602060405180830381600087803b15801561016e57600080fd5b505af1158015610182573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101a69190610265565b6101b091906102bc565b6101ba91906102bc565b9050600a8114156101da57600a816101d291906102bc565b915050610209565b60198114156101f8576002816101f09190610312565b915050610209565b600a816102059190610312565b9150505b919050565b60008135905061021d816103aa565b92915050565b600081519050610232816103aa565b92915050565b60006020828403121561024e5761024d6103a5565b5b600061025c8482850161020e565b91505092915050565b60006020828403121561027b5761027a6103a5565b5b600061028984828501610223565b91505092915050565b61029b8161036c565b82525050565b60006020820190506102b66000830184610292565b92915050565b60006102c78261036c565b91506102d28361036c565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561030757610306610376565b5b828201905092915050565b600061031d8261036c565b91506103288361036c565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561036157610360610376565b5b828202905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b6103b38161036c565b81146103be57600080fd5b5056fea2646970667358221220f0c85d3c193689a686ebf30f33bdac178593d7fe1036cce209c44c7833209cb464736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0xc538a4c3f414cbf7f78373e253c1beadd6310d45", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "0x29e99f070000000000000000000000000000000000000000000000000000000000000006", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x29e99f070000000000000000000000000000000000000000000000000000000000000006", + "name": "", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }] +} diff --git a/tests/testdata/concolic_io/multiple_example.sol b/tests/testdata/concolic_io/multiple_example.sol new file mode 100644 index 00000000..116239c6 --- /dev/null +++ b/tests/testdata/concolic_io/multiple_example.sol @@ -0,0 +1,25 @@ +// source of the bytecode, as a reference for the test. +pragma solidity 0.8.6; + +contract Example1 { + uint256 private initialized = 0; + uint256 public count = 1; + + function init() public { + initialized = 1; + } + + function run(uint256 input, uint val) public { + if (val == 3) { + count += input; + } + else if(val == 5) { + count += 2 * input; + } + else if(val == 7) { + count += 10 + input; + } + else + count++; + } +} diff --git a/tests/testdata/concolic_io/multiple_example_input.json b/tests/testdata/concolic_io/multiple_example_input.json new file mode 100644 index 00000000..d3ac0479 --- /dev/null +++ b/tests/testdata/concolic_io/multiple_example_input.json @@ -0,0 +1,48 @@ +{ + "initialState": { + "accounts": { + "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe": { + "balance": "0x10000", + "code": "", + "nonce": 0, + "storage": {} + }, + "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef": { + "balance": "0x0", + "code": "", + "nonce": 0, + "storage": {} + } + } + }, + "steps": [{ + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x6080604052600080556001805534801561001857600080fd5b50610349806100286000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd146100465780637357f5d214610064578063e1c7392a14610080575b600080fd5b61004e61008a565b60405161005b91906101aa565b60405180910390f35b61007e6004803603810190610079919061015b565b610090565b005b61008861013c565b005b60015481565b60038114156100b75781600160008282546100ab91906101c5565b92505081905550610138565b60058114156100ea578160026100cd919061021b565b600160008282546100de91906101c5565b92505081905550610137565b600781141561011d5781600a61010091906101c5565b6001600082825461011191906101c5565b92505081905550610136565b600160008154809291906101309061027f565b91905055505b5b5b5050565b6001600081905550565b600081359050610155816102fc565b92915050565b60008060408385031215610172576101716102f7565b5b600061018085828601610146565b925050602061019185828601610146565b9150509250929050565b6101a481610275565b82525050565b60006020820190506101bf600083018461019b565b92915050565b60006101d082610275565b91506101db83610275565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156102105761020f6102c8565b5b828201905092915050565b600061022682610275565b915061023183610275565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561026a576102696102c8565b5b828202905092915050565b6000819050919050565b600061028a82610275565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156102bd576102bc6102c8565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61030581610275565b811461031057600080fd5b5056fea26469706673582212202f9c24200282a71edb4cfb1018bca4a5608551f6bf5910ca1569133908c2605464736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0x901d12ebe1b195e5aa8748e62bd7734ae19b51f", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "0x7357f5d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x7357f5d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "name": "run(uint256,uint256)", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }] +} diff --git a/tests/testdata/concolic_io/simple_example.sol b/tests/testdata/concolic_io/simple_example.sol new file mode 100644 index 00000000..e57396e9 --- /dev/null +++ b/tests/testdata/concolic_io/simple_example.sol @@ -0,0 +1,19 @@ +// source of the bytecode, as a reference for the test. +pragma solidity 0.8.6; + +contract Example1 { + uint256 private initialized = 0; + uint256 public count = 1; + + function init() public { + initialized = 1; + } + + function run(uint256 input, uint val) public { + if (val == 3) { + count += input; + } + else + count++; + } +} diff --git a/tests/testdata/concolic_io/simple_example_input.json b/tests/testdata/concolic_io/simple_example_input.json new file mode 100644 index 00000000..2d60feff --- /dev/null +++ b/tests/testdata/concolic_io/simple_example_input.json @@ -0,0 +1,48 @@ +{ + "initialState": { + "accounts": { + "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe": { + "balance": "0x10000", + "code": "", + "nonce": 0, + "storage": {} + }, + "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef": { + "balance": "0x0", + "code": "", + "nonce": 0, + "storage": {} + } + } + }, + "steps": [{ + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x6080604052600080556001805534801561001857600080fd5b50610287806100286000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806306661abd146100465780637357f5d214610064578063e1c7392a14610080575b600080fd5b61004e61008a565b60405161005b9190610142565b60405180910390f35b61007e600480360381019061007991906100f3565b610090565b005b6100886100d4565b005b60015481565b60038114156100b75781600160008282546100ab919061015d565b925050819055506100d0565b600160008154809291906100ca906101bd565b91905055505b5050565b6001600081905550565b6000813590506100ed8161023a565b92915050565b6000806040838503121561010a57610109610235565b5b6000610118858286016100de565b9250506020610129858286016100de565b9150509250929050565b61013c816101b3565b82525050565b60006020820190506101576000830184610133565b92915050565b6000610168826101b3565b9150610173836101b3565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156101a8576101a7610206565b5b828201905092915050565b6000819050919050565b60006101c8826101b3565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156101fb576101fa610206565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b610243816101b3565b811461024e57600080fd5b5056fea2646970667358221220de20d82abee4e4f81661465126dda93179255f9249d58bde7c3ea5f5f8bc3dbb64736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0x901d12ebe1b195e5aa8748e62bd7734ae19b51f", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "0x7357f5d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x7357f5d2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "name": "run(uint256,uint256)", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }] +} diff --git a/tests/testdata/concolic_io/two_contract.sol b/tests/testdata/concolic_io/two_contract.sol new file mode 100644 index 00000000..9debf6e2 --- /dev/null +++ b/tests/testdata/concolic_io/two_contract.sol @@ -0,0 +1,42 @@ +pragma solidity 0.8.6; + +/** + * @title Storage + * @dev Store & retreive value in a variable + */ +contract D1 { + + uint256 number; + + function store(uint256 num) public { + assert(num < 5); + number =num; + } + + function retval() public returns(uint256){ + return number; + } +} + +contract D2 { + D1 d1; + constructor() public + { + d1 = D1(0x0901d12ebE1b195E5AA8748E62Bd7734aE19B51F); + } + function test(uint256 num) public returns(uint256) { + uint256 sum = d1.retval() + num; + if (sum == 10) { + return sum + 10; + } + else if(sum == 11) { + return sum + 12; + } + else if(sum == 30) { + return sum * 2; + } + assert(sum != 20); + return sum; + } +} + diff --git a/tests/testdata/concolic_io/two_contract_input.json b/tests/testdata/concolic_io/two_contract_input.json new file mode 100644 index 00000000..58eb0908 --- /dev/null +++ b/tests/testdata/concolic_io/two_contract_input.json @@ -0,0 +1,78 @@ +{ + "initialState": { + "accounts": { + "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe": { + "balance": "0x10000000000000000000000000000", + "code": "", + "nonce": 0, + "storage": {} + }, + "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef": { + "balance": "0x0", + "code": "", + "nonce": 0, + "storage": {} + } + } + }, + "steps": [{ + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x608060405234801561001057600080fd5b50610190806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80636057361d1461003b5780639c88345314610057575b600080fd5b610055600480360381019061005091906100ae565b610075565b005b61005f610090565b60405161006c91906100ea565b60405180910390f35b600581106100865761008561010f565b5b8060008190555050565b60008054905090565b6000813590506100a881610143565b92915050565b6000602082840312156100c4576100c361013e565b5b60006100d284828501610099565b91505092915050565b6100e481610105565b82525050565b60006020820190506100ff60008301846100db565b92915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600080fd5b61014c81610105565b811461015757600080fd5b5056fea26469706673582212200d02c19e8a2bae5958a822a8cfde054e94c216f2113e4b13c9dfcebaa7fd6d1564736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0x0901d12ebE1b195E5AA8748E62Bd7734aE19B51F", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x6057361d0000000000000000000000000000000000000000000000000000000000000001", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x608060405234801561001057600080fd5b50730901d12ebe1b195e5aa8748e62bd7734ae19b51f6000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061039e806100746000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806329e99f0714610030575b600080fd5b61004a600480360381019061004591906101b0565b610060565b6040516100579190610219565b60405180910390f35b6000808260008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16639c8834536040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156100cc57600080fd5b505af11580156100e0573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061010491906101dd565b61010e9190610234565b9050600a81141561012e57600a816101269190610234565b915050610181565b600b81141561014c57600c816101449190610234565b915050610181565b601e81141561016a57600281610162919061028a565b915050610181565b601481141561017c5761017b6102ee565b5b809150505b919050565b60008135905061019581610351565b92915050565b6000815190506101aa81610351565b92915050565b6000602082840312156101c6576101c561034c565b5b60006101d484828501610186565b91505092915050565b6000602082840312156101f3576101f261034c565b5b60006102018482850161019b565b91505092915050565b610213816102e4565b82525050565b600060208201905061022e600083018461020a565b92915050565b600061023f826102e4565b915061024a836102e4565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0382111561027f5761027e61031d565b5b828201905092915050565b6000610295826102e4565b91506102a0836102e4565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04831182151516156102d9576102d861031d565b5b828202905092915050565b6000819050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600080fd5b61035a816102e4565b811461036557600080fd5b5056fea26469706673582212205af1f2d3a496abf9b24e8953ec8c2869d565a0a6057c1ff82097fbff424f6e3464736f6c63430008060033", + "name": "unknown", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }, + { + "address": "0x384f682f4a5AbefC8795Cc38a340dE9446dFAE7A", + "blockCoinbase": "0xcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcbcb", + "blockDifficulty": "0xa7d7343662e26", + "blockGasLimit": "0x7d0000", + "blockNumber": "0x66e393", + "blockTime": "0x5bfa4639", + "calldata": "0x29e99f07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x7d000", + "gasPrice": "0x773594000", + "input": "0x29e99f07000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "name": "", + "origin": "0xaffeaffeaffeaffeaffeaffeaffeaffeaffeaffe", + "value": "0x0" + }] +} diff --git a/tests/testdata/inputs/coverage.sol.o b/tests/testdata/inputs/coverage.sol.o new file mode 100644 index 00000000..e9309c91 --- /dev/null +++ b/tests/testdata/inputs/coverage.sol.o @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610340806100606000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063228cb733146100725780633eb6a67e1461009d5780638da5cb5b146100a7578063ae169a50146100fe578063e834a8341461012b575b600080fd5b34801561007e57600080fd5b5061008761015a565b6040518082815260200191505060405180910390f35b6100a5610160565b005b3480156100b357600080fd5b506100bc61024a565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561010a57600080fd5b506101296004803603810190808035906020019092919050505061026f565b005b34801561013757600080fd5b50610140610301565b604051808215151515815260200191505060405180910390f35b60015481565b600060149054906101000a900460ff1615151561017c57600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156101d757600080fd5b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc6001549081150290604051600060405180830381858888f19350505050158015610240573d6000803e3d6000fd5b5034600181905550565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060149054906101000a900460ff1615151561028b57600080fd5b600a8110151561029a57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff166108fc6001549081150290604051600060405180830381858888f193505050501580156102e2573d6000803e3d6000fd5b506001600060146101000a81548160ff02191690831515021790555050565b600060149054906101000a900460ff16815600a165627a7a723058207bb1caf5a53e1b9c9895f50b1740af67a18d256f4c2724858bdcd660db1729310029 \ No newline at end of file