From 37d98891d5c9e20da02d5b92bea26533172a4743 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 16 Oct 2018 19:33:28 +0530 Subject: [PATCH 01/42] Add mypy to circleci --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 351fc89b..615e7625 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,6 +52,11 @@ jobs: - store_artifacts: path: /home/mythril/.tox/output + - run: + name: Type testing + command: mypy . + working_directory: /home/mythril + - run: name: Ensuring that setup script is functional command: python3 setup.py install From 021f1ed6e97dabd16f013941110a91a8537f1336 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 16 Oct 2018 19:45:30 +0530 Subject: [PATCH 02/42] install mypy --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 615e7625..fd6ad64e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,7 +54,7 @@ jobs: - run: name: Type testing - command: mypy . + command: pip3 install mypy; mypy . working_directory: /home/mythril - run: From 6696f26383cbe1a0ae1226fa1657088533ef1593 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 16 Oct 2018 19:56:50 +0530 Subject: [PATCH 03/42] install mypy through apt-get --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fd6ad64e..1199f2fd 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,7 +54,7 @@ jobs: - run: name: Type testing - command: pip3 install mypy; mypy . + command: sudo apt-get install mypy;pip3 install -r requirements.txt; mypy . working_directory: /home/mythril - run: From 5eb6c0781ab62ecee6504e3885019608443d0c53 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 17 Oct 2018 18:03:46 +0530 Subject: [PATCH 04/42] Remove sudo --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1199f2fd..c7718b86 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -54,7 +54,7 @@ jobs: - run: name: Type testing - command: sudo apt-get install mypy;pip3 install -r requirements.txt; mypy . + command: apt-get install mypy;pip3 install -r requirements.txt; mypy . working_directory: /home/mythril - run: From 7035959b23d8b4946e3bf18bfcc9afa3548fe6a1 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 17 Oct 2018 18:16:17 +0530 Subject: [PATCH 05/42] Add the mypy to tox --- .circleci/config.yml | 5 ----- tox.ini | 3 +++ 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c7718b86..351fc89b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,11 +52,6 @@ jobs: - store_artifacts: path: /home/mythril/.tox/output - - run: - name: Type testing - command: apt-get install mypy;pip3 install -r requirements.txt; mypy . - working_directory: /home/mythril - - run: name: Ensuring that setup script is functional command: python3 setup.py install diff --git a/tox.ini b/tox.ini index 49a52d9d..823129ed 100644 --- a/tox.ini +++ b/tox.ini @@ -33,6 +33,9 @@ commands = --junitxml={toxworkdir}/output/{envname}/junit.xml \ {posargs} + mypy --follow-imports=silent --warn-unused-ignores . + + [coverage:report] omit = *__init__.py From aa368ade85295979891b070a3cfa29126f9195c3 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 17 Oct 2018 18:23:31 +0530 Subject: [PATCH 06/42] Add the mypy to setup.py --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 2c92ce4a..3f6941f4 100755 --- a/setup.py +++ b/setup.py @@ -108,6 +108,7 @@ setup( ], tests_require=[ + 'mypy', 'pytest>=3.6.0', 'pytest_mock', 'pytest-cov' From dd661e9d929545c3afc7f58a776afc67901903ac Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 17 Oct 2018 18:33:18 +0530 Subject: [PATCH 07/42] add mypy to deps --- setup.py | 2 +- tox.ini | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 3f6941f4..1300bc36 100755 --- a/setup.py +++ b/setup.py @@ -111,7 +111,7 @@ setup( 'mypy', 'pytest>=3.6.0', 'pytest_mock', - 'pytest-cov' + 'pytest-cov', ], python_requires='>=3.5', diff --git a/tox.ini b/tox.ini index 823129ed..a9bce26a 100644 --- a/tox.ini +++ b/tox.ini @@ -18,11 +18,13 @@ basepython = python3.6 setenv = COVERAGE_FILE = .coverage.{envname} deps = + mypy pytest pytest-mock pytest-cov whitelist_externals = mkdir commands = + mypy --follow-imports=silent --warn-unused-ignores . mkdir -p {toxinidir}/tests/testdata/outputs_current/ mkdir -p {toxinidir}/tests/testdata/outputs_current_laser_result/ py.test -v \ @@ -33,7 +35,6 @@ commands = --junitxml={toxworkdir}/output/{envname}/junit.xml \ {posargs} - mypy --follow-imports=silent --warn-unused-ignores . [coverage:report] From e3e650553d5ec612c730370e4af18e1764d8c00b Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 17 Oct 2018 18:33:38 +0530 Subject: [PATCH 08/42] add mypy to deps --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1300bc36..3f6941f4 100755 --- a/setup.py +++ b/setup.py @@ -111,7 +111,7 @@ setup( 'mypy', 'pytest>=3.6.0', 'pytest_mock', - 'pytest-cov', + 'pytest-cov' ], python_requires='>=3.5', From b6b62f8f2bcd35b2e68b523b8b914fef6cb1cc28 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 17 Oct 2018 18:38:13 +0530 Subject: [PATCH 09/42] Ignore missing imports for mypy --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index a9bce26a..249480e2 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = pytest-cov whitelist_externals = mkdir commands = - mypy --follow-imports=silent --warn-unused-ignores . + mypy --follow-imports=silent --warn-unused-ignores --ignore-missing-imports --no-strict-optional . mkdir -p {toxinidir}/tests/testdata/outputs_current/ mkdir -p {toxinidir}/tests/testdata/outputs_current_laser_result/ py.test -v \ From 5ae1052bdf3cad3c1814875f110d7482faf72115 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 13:40:20 +0530 Subject: [PATCH 10/42] reformat setup.py with black --- setup.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/setup.py b/setup.py index 9c833e63..f1724d95 100755 --- a/setup.py +++ b/setup.py @@ -98,13 +98,7 @@ setup( "persistent>=4.2.0", "ethereum-input-decoder>=0.2.2", ], - - tests_require=[ - 'mypy', - 'pytest>=3.6.0', - 'pytest_mock', - 'pytest-cov' - ], + tests_require=["mypy", "pytest>=3.6.0", "pytest_mock", "pytest-cov"], python_requires=">=3.5", extras_require={}, package_data={"mythril.analysis.templates": ["*"], "": ["signatures.db"]}, From a25d782b908545185c4f4c9f9117a39577be8b5e Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 16:25:32 +0530 Subject: [PATCH 11/42] Fix type hinting to mythril module and fix typehints in svm, disassembler, state modules --- mythril/analysis/modules/base.py | 5 +-- mythril/disassembler/asm.py | 2 +- mythril/disassembler/disassembly.py | 11 +++--- mythril/laser/ethereum/cfg.py | 11 +++--- mythril/laser/ethereum/natives.py | 11 +++--- mythril/laser/ethereum/state/machine_state.py | 4 +-- mythril/laser/ethereum/svm.py | 35 ++++++++----------- mythril/laser/ethereum/util.py | 18 ++++------ mythril/support/signatures.py | 6 ++-- tox.ini | 2 +- 10 files changed, 51 insertions(+), 54 deletions(-) diff --git a/mythril/analysis/modules/base.py b/mythril/analysis/modules/base.py index 072f74e9..d7075109 100644 --- a/mythril/analysis/modules/base.py +++ b/mythril/analysis/modules/base.py @@ -3,6 +3,7 @@ modules.""" import logging from typing import List +from mythril.analysis.report import Issue log = logging.getLogger(__name__) @@ -21,7 +22,7 @@ class DetectionModule: entrypoint: str = "post", pre_hooks: List[str] = None, post_hooks: List[str] = None, - ): + ) -> None: self.name = name self.swc_id = swc_id self.pre_hooks = pre_hooks if pre_hooks else [] @@ -33,7 +34,7 @@ class DetectionModule: self.name, ) self.entrypoint = entrypoint - self._issues = [] + self._issues = [] # type: List[Issue] @property def issues(self): diff --git a/mythril/disassembler/asm.py b/mythril/disassembler/asm.py index 2c9c7907..cbbe1881 100644 --- a/mythril/disassembler/asm.py +++ b/mythril/disassembler/asm.py @@ -90,7 +90,7 @@ def is_sequence_match(pattern: list, instruction_list: list, index: int) -> bool return True -def disassemble(bytecode: str) -> list: +def disassemble(bytecode: bytes) -> list: """Disassembles evm bytecode and returns a list of instructions. :param bytecode: diff --git a/mythril/disassembler/disassembly.py b/mythril/disassembler/disassembly.py index 0d4828fc..1d5bb601 100644 --- a/mythril/disassembler/disassembly.py +++ b/mythril/disassembler/disassembly.py @@ -3,6 +3,8 @@ from mythril.ethereum import util from mythril.disassembler import asm from mythril.support.signatures import SignatureDB +from typing import Dict, List + class Disassembly(object): """Disassembly class. @@ -14,7 +16,7 @@ class Disassembly(object): - function entry point to function name mapping """ - def __init__(self, code: str, enable_online_lookup: bool = False): + def __init__(self, code: str, enable_online_lookup: bool = False) -> None: """ :param code: @@ -23,9 +25,9 @@ class Disassembly(object): self.bytecode = code self.instruction_list = asm.disassemble(util.safe_decode(code)) - self.func_hashes = [] - self.function_name_to_address = {} - self.address_to_function_name = {} + self.func_hashes = [] # type: List[str] + self.function_name_to_address = {} # type: Dict[str, int] + self.address_to_function_name = {} # type: Dict[int, str] # open from default locations # control if you want to have online signature hash lookups @@ -41,7 +43,6 @@ class Disassembly(object): index, self.instruction_list, signatures ) self.func_hashes.append(function_hash) - if jump_target is not None and function_name is not None: self.function_name_to_address[function_name] = jump_target self.address_to_function_name[jump_target] = function_name diff --git a/mythril/laser/ethereum/cfg.py b/mythril/laser/ethereum/cfg.py index 056692e5..77637728 100644 --- a/mythril/laser/ethereum/cfg.py +++ b/mythril/laser/ethereum/cfg.py @@ -1,9 +1,12 @@ """This module.""" from enum import Enum -from typing import Dict +from typing import Dict, List, TYPE_CHECKING from flags import Flags +if TYPE_CHECKING: + from mythril.laser.ethereum.state.global_state import GlobalState + gbl_next_uid = 0 # node counter @@ -33,7 +36,7 @@ class Node: start_addr=0, constraints=None, function_name="unknown", - ): + ) -> None: """ :param contract_name: @@ -43,7 +46,7 @@ class Node: constraints = constraints if constraints else [] self.contract_name = contract_name self.start_addr = start_addr - self.states = [] + self.states = [] # type: List[GlobalState] self.constraints = constraints self.function_name = function_name self.flags = NodeFlags() @@ -86,7 +89,7 @@ class Edge: node_to: int, edge_type=JumpType.UNCONDITIONAL, condition=None, - ): + ) -> None: """ :param node_from: diff --git a/mythril/laser/ethereum/natives.py b/mythril/laser/ethereum/natives.py index 205e325a..fd801288 100644 --- a/mythril/laser/ethereum/natives.py +++ b/mythril/laser/ethereum/natives.py @@ -9,7 +9,8 @@ from py_ecc.secp256k1 import N as secp256k1n from rlp.utils import ALL_BYTES from mythril.laser.ethereum.state.calldata import BaseCalldata, ConcreteCalldata -from mythril.laser.ethereum.util import bytearray_to_int, sha3 +from mythril.laser.ethereum.util import bytearray_to_int +from ethereum.utils import sha3 from mythril.laser.smt import Concat, simplify log = logging.getLogger(__name__) @@ -50,7 +51,7 @@ def extract32(data: bytearray, i: int) -> int: return bytearray_to_int(o) -def ecrecover(data: Union[bytes, str, List[int]]) -> bytes: +def ecrecover(data: List[int]) -> List[int]: """ :param data: @@ -77,7 +78,7 @@ def ecrecover(data: Union[bytes, str, List[int]]) -> bytes: return o -def sha256(data: Union[bytes, str, List[int]]) -> bytes: +def sha256(data: List[int]) -> List[int]: """ :param data: @@ -90,7 +91,7 @@ def sha256(data: Union[bytes, str, List[int]]) -> bytes: return hashlib.sha256(data).digest() -def ripemd160(data: Union[bytes, str, List[int]]) -> bytes: +def ripemd160(data: List[int]) -> bytes: """ :param data: @@ -105,7 +106,7 @@ def ripemd160(data: Union[bytes, str, List[int]]) -> bytes: return bytes(padded) -def identity(data: Union[bytes, str, List[int]]) -> bytes: +def identity(data: List[int]) -> List[int]: """ :param data: diff --git a/mythril/laser/ethereum/state/machine_state.py b/mythril/laser/ethereum/state/machine_state.py index 45e824db..33e7bfe2 100644 --- a/mythril/laser/ethereum/state/machine_state.py +++ b/mythril/laser/ethereum/state/machine_state.py @@ -20,7 +20,7 @@ class MachineStack(list): STACK_LIMIT = 1024 - def __init__(self, default_list=None): + def __init__(self, default_list=None) -> None: """ :param default_list: @@ -95,7 +95,7 @@ class MachineState: depth=0, max_gas_used=0, min_gas_used=0, - ): + ) -> None: """Constructor for machineState. :param gas_limit: diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index 46ab6f19..488ba82a 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -4,7 +4,7 @@ from collections import defaultdict from copy import copy from datetime import datetime, timedelta from functools import reduce -from typing import Callable, Dict, List, Tuple, Union +from typing import Callable, Dict, DefaultDict, List, Tuple, Union from mythril import alarm from mythril.exceptions import OutOfTimeError @@ -56,7 +56,7 @@ class LaserEVM: transaction_count=2, requires_statespace=True, enable_iprof=False, - ): + ) -> None: """ :param accounts: @@ -73,12 +73,12 @@ class LaserEVM: self.world_state = world_state self.open_states = [world_state] - self.coverage = {} + self.coverage = {} # type: Dict[str, Tuple[int, List[bool]]] self.total_states = 0 self.dynamic_loader = dynamic_loader - self.work_list = [] + self.work_list = [] # type: List[GlobalState] self.strategy = strategy(self.work_list, max_depth) self.max_depth = max_depth self.transaction_count = transaction_count @@ -88,13 +88,13 @@ class LaserEVM: self.requires_statespace = requires_statespace if self.requires_statespace: - self.nodes = {} - self.edges = [] + self.nodes = {} # type: Dict[int, Node] + self.edges = [] # type: List[Edge] - self.time = None + self.time = None # type: datetime - self.pre_hooks = defaultdict(list) - self.post_hooks = defaultdict(list) + self.pre_hooks = defaultdict(list) # type: DefaultDict[str, List[Callable]] + self.post_hooks = defaultdict(list) # type: DefaultDict[str, List[Callable]] self.iprof = InstructionProfiler() if enable_iprof else None @@ -177,11 +177,8 @@ class LaserEVM: self.total_states, ) for code, coverage in self.coverage.items(): - cov = ( - reduce(lambda sum_, val: sum_ + 1 if val else sum_, coverage[1]) - / float(coverage[0]) - * 100 - ) + cov = sum(coverage[1]) / float(coverage[0]) * 100 + log.info("Achieved {:.2f}% coverage for code: {}".format(cov, code)) if self.iprof is not None: @@ -222,9 +219,7 @@ class LaserEVM: """ total_covered_instructions = 0 for _, cv in self.coverage.items(): - total_covered_instructions += reduce( - lambda sum_, val: sum_ + 1 if val else sum_, cv[1] - ) + total_covered_instructions += sum(cv[1]) return total_covered_instructions def exec(self, create=False, track_gas=False) -> Union[List[GlobalState], None]: @@ -234,7 +229,7 @@ class LaserEVM: :param track_gas: :return: """ - final_states = [] + final_states = [] # type: List[GlobalState] for global_state in self.strategy: if ( self.create_timeout @@ -389,10 +384,10 @@ class LaserEVM: instruction_index = global_state.mstate.pc if code not in self.coverage.keys(): - self.coverage[code] = [ + self.coverage[code] = ( number_of_instructions, [False] * number_of_instructions, - ] + ) self.coverage[code][1][instruction_index] = True diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 880e04c4..31bcfbc0 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -1,9 +1,10 @@ """This module contains various utility conversion functions and constants for LASER.""" import re -from typing import Dict, List, Union +from typing import Dict, List, Union, TYPE_CHECKING -import sha3 as _sha3 +if TYPE_CHECKING: + from mythril.laser.ethereum.state.machine_state import MachineState from mythril.laser.smt import BitVec, Bool, Expression, If, simplify, symbol_factory @@ -12,15 +13,6 @@ TT256M1 = 2 ** 256 - 1 TT255 = 2 ** 255 -def sha3(seed: str) -> bytes: - """ - - :param seed: - :return: - """ - return _sha3.keccak_256(bytes(seed)).digest() - - def safe_decode(hex_encoded_string: str) -> bytes: """ @@ -117,7 +109,9 @@ def get_concrete_int(item: Union[int, Expression]) -> int: return value -def concrete_int_from_bytes(concrete_bytes: bytes, start_index: int) -> int: +def concrete_int_from_bytes( + concrete_bytes: List[Union[BitVec, int]], start_index: int +) -> int: """ :param concrete_bytes: diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index 530f0137..1e749fbe 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -7,7 +7,7 @@ import sqlite3 import time from collections import defaultdict from subprocess import PIPE, Popen -from typing import List +from typing import List, Dict, Set from mythril.exceptions import CompilerError @@ -45,7 +45,7 @@ def synchronized(sync_lock): class Singleton(type): """A metaclass type implementing the singleton pattern.""" - _instances = {} + _instances = dict() @synchronized(lock) def __call__(cls, *args, **kwargs): @@ -60,6 +60,8 @@ class Singleton(type): """ if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) + print(type(cls), cls._instances[cls]) + return cls._instances[cls] diff --git a/tox.ini b/tox.ini index 7dc6ac1b..93ee48a2 100644 --- a/tox.ini +++ b/tox.ini @@ -26,7 +26,7 @@ deps = passenv = MYTHRIL_DIR = {homedir} whitelist_externals = mkdir commands = - mypy --follow-imports=silent --warn-unused-ignores --ignore-missing-imports --no-strict-optional . + mypy --follow-imports=silent --warn-unused-ignores --ignore-missing-imports --no-strict-optional mythril mkdir -p {toxinidir}/tests/testdata/outputs_current/ mkdir -p {toxinidir}/tests/testdata/outputs_current_laser_result/ py.test -v \ From b57017832c3184027a42aeb59d514116df12cec6 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 16:35:19 +0530 Subject: [PATCH 12/42] Fix all svm errors in mypy --- mythril/laser/ethereum/cfg.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mythril/laser/ethereum/cfg.py b/mythril/laser/ethereum/cfg.py index 77637728..39638987 100644 --- a/mythril/laser/ethereum/cfg.py +++ b/mythril/laser/ethereum/cfg.py @@ -23,6 +23,9 @@ class JumpType(Enum): class NodeFlags(Flags): """A collection of flags to denote the type a call graph node can have.""" + def __or__(self, other) -> "NodeFlags": + return super().__or__(self, other) + FUNC_ENTRY = 1 CALL_RETURN = 2 From 35ddf21b9448b53d6af7a773ea733fe0585b6a36 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 17:15:04 +0530 Subject: [PATCH 13/42] Fix most of the type hint errors in instructions.py file --- mythril/laser/ethereum/cfg.py | 2 +- mythril/laser/ethereum/gas.py | 3 ++- mythril/laser/ethereum/instructions.py | 20 ++++++++++---------- mythril/laser/ethereum/state/account.py | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mythril/laser/ethereum/cfg.py b/mythril/laser/ethereum/cfg.py index 39638987..0578c602 100644 --- a/mythril/laser/ethereum/cfg.py +++ b/mythril/laser/ethereum/cfg.py @@ -24,7 +24,7 @@ class NodeFlags(Flags): """A collection of flags to denote the type a call graph node can have.""" def __or__(self, other) -> "NodeFlags": - return super().__or__(self, other) + return super().__or__(other) FUNC_ENTRY = 1 CALL_RETURN = 2 diff --git a/mythril/laser/ethereum/gas.py b/mythril/laser/ethereum/gas.py index 43c5d48b..8fae5f53 100644 --- a/mythril/laser/ethereum/gas.py +++ b/mythril/laser/ethereum/gas.py @@ -2,6 +2,7 @@ table.""" from ethereum import opcodes from ethereum.utils import ceil32 +from typing import Callable, Dict, Tuple, Union def calculate_native_gas(size: int, contract: str): @@ -185,4 +186,4 @@ OPCODE_GAS = { "SUICIDE": (5000, 30000), "ASSERT_FAIL": (0, 0), "INVALID": (0, 0), -} +} # type: Dict[str, Union[Tuple[int, int], Callable]] diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 95728185..2889c92d 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -4,7 +4,7 @@ import binascii import logging from copy import copy, deepcopy -from typing import Callable, List, Union +from typing import cast, Callable, List, Union, Tuple from datetime import datetime from ethereum import utils @@ -127,7 +127,7 @@ class StateTransition(object): if not self.enable_gas: return global_state opcode = global_state.instruction["opcode"] - min_gas, max_gas = OPCODE_GAS[opcode] + min_gas, max_gas = cast(Tuple[int, int], OPCODE_GAS[opcode]) global_state.mstate.min_gas_used += min_gas global_state.mstate.max_gas_used += max_gas return global_state @@ -155,7 +155,7 @@ class Instruction: """Instruction class is used to mutate a state according to the current instruction.""" - def __init__(self, op_code: str, dynamic_loader: DynLoader, iprof=None): + def __init__(self, op_code: str, dynamic_loader: DynLoader, iprof=None) -> None: """ :param op_code: @@ -1253,12 +1253,12 @@ class Instruction: except TypeError: if not keccak_function_manager.is_keccak(index): - return self._sload_helper(global_state, str(index)) + return self._sload_helper(global_state, index) storage_keys = global_state.environment.active_account.storage.keys() keccak_keys = list(filter(keccak_function_manager.is_keccak, storage_keys)) - results = [] + results = [] # type: List[GlobalState] constraints = [] for keccak_key in keccak_keys: @@ -1281,11 +1281,11 @@ class Instruction: if len(results) > 0: return results - return self._sload_helper(global_state, str(index)) + return self._sload_helper(global_state, int(index)) @staticmethod def _sload_helper( - global_state: GlobalState, index: Union[int, Expression], constraints=None + global_state: GlobalState, index: int, constraints=None ): """ @@ -1344,7 +1344,7 @@ class Instruction: storage_keys = global_state.environment.active_account.storage.keys() keccak_keys = filter(keccak_function_manager.is_keccak, storage_keys) - results = [] + results = [] # type: List[GlobalState] new = symbol_factory.Bool(False) for keccak_key in keccak_keys: @@ -1870,12 +1870,12 @@ class Instruction: try: memory_out_offset = ( util.get_concrete_int(memory_out_offset) - if isinstance(memory_out_offset, ExprRef) + if isinstance(memory_out_offset, Expression) else memory_out_offset ) memory_out_size = ( util.get_concrete_int(memory_out_size) - if isinstance(memory_out_size, ExprRef) + if isinstance(memory_out_size, Expression) else memory_out_size ) except TypeError: diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index 2c69dbc5..cca03a09 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -51,7 +51,7 @@ class Storage: self._storage[item] = symbol_factory.BitVecVal(0, 256) return self._storage[item] - def __setitem__(self, key: str, value: ExprRef) -> None: + def __setitem__(self, key: int, value: ExprRef) -> None: self._storage[key] = value def keys(self) -> KeysView: From 1f1d6f17d7ea2c5b90e55cb754b6f86fe62c3a6e Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 22:56:19 +0530 Subject: [PATCH 14/42] Fix some more type hints for analysis and laser modules --- mythril/analysis/modules/delegatecall.py | 4 ++-- .../analysis/modules/dependence_on_predictable_vars.py | 3 ++- mythril/analysis/modules/integer.py | 10 +++++----- mythril/analysis/modules/multiple_sends.py | 5 +++-- mythril/analysis/modules/unchecked_retval.py | 9 ++++++--- mythril/laser/ethereum/call.py | 2 +- mythril/laser/ethereum/instructions.py | 8 ++++---- mythril/laser/ethereum/state/account.py | 2 +- mythril/laser/ethereum/state/environment.py | 2 +- mythril/laser/ethereum/state/global_state.py | 8 ++++++-- mythril/laser/ethereum/state/world_state.py | 4 ++-- mythril/laser/ethereum/util.py | 2 +- 12 files changed, 34 insertions(+), 25 deletions(-) diff --git a/mythril/analysis/modules/delegatecall.py b/mythril/analysis/modules/delegatecall.py index 596f8ce5..9ebfcd17 100644 --- a/mythril/analysis/modules/delegatecall.py +++ b/mythril/analysis/modules/delegatecall.py @@ -17,7 +17,7 @@ log = logging.getLogger(__name__) class DelegateCallModule(DetectionModule): """This module detects calldata being forwarded using DELEGATECALL.""" - def __init__(self): + def __init__(self) -> None: """""" super().__init__( name="DELEGATECALL Usage in Fallback Function", @@ -46,7 +46,7 @@ def _analyze_states(state: GlobalState) -> List[Issue]: call = get_call_from_state(state) if call is None: return [] - issues = [] + issues = [] # type: List[Issue] if call.type is not "DELEGATECALL": return [] diff --git a/mythril/analysis/modules/dependence_on_predictable_vars.py b/mythril/analysis/modules/dependence_on_predictable_vars.py index 632672b8..fc965307 100644 --- a/mythril/analysis/modules/dependence_on_predictable_vars.py +++ b/mythril/analysis/modules/dependence_on_predictable_vars.py @@ -19,7 +19,7 @@ class PredictableDependenceModule(DetectionModule): """This module detects whether Ether is sent using predictable parameters.""" - def __init__(self): + def __init__(self) -> None: """""" super().__init__( name="Dependence of Predictable Variables", @@ -119,6 +119,7 @@ def _analyze_states(state: GlobalState) -> list: if m and solve(call): found = m.group(1) + print(type(found)) if found: # block.blockhash(block.number - N) description = ( diff --git a/mythril/analysis/modules/integer.py b/mythril/analysis/modules/integer.py index c11b40ed..ae0a12d3 100644 --- a/mythril/analysis/modules/integer.py +++ b/mythril/analysis/modules/integer.py @@ -2,7 +2,7 @@ underflows.""" import json - +from typing import Dict from mythril.analysis import solver from mythril.analysis.report import Issue from mythril.analysis.swc_data import INTEGER_OVERFLOW_AND_UNDERFLOW @@ -27,7 +27,7 @@ log = logging.getLogger(__name__) class OverUnderflowAnnotation: - def __init__(self, overflowing_state: GlobalState, operator: str, constraint): + def __init__(self, overflowing_state: GlobalState, operator: str, constraint) -> None: self.overflowing_state = overflowing_state self.operator = operator self.constraint = constraint @@ -36,7 +36,7 @@ class OverUnderflowAnnotation: class IntegerOverflowUnderflowModule(DetectionModule): """This module searches for integer over- and underflows.""" - def __init__(self): + def __init__(self) -> None: """""" super().__init__( name="Integer Overflow and Underflow", @@ -49,8 +49,8 @@ class IntegerOverflowUnderflowModule(DetectionModule): entrypoint="callback", pre_hooks=["ADD", "MUL", "SUB", "SSTORE", "JUMPI"], ) - self._overflow_cache = {} - self._underflow_cache = {} + self._overflow_cache = {} # type: Dict[int, bool] + self._underflow_cache = {} # type: Dict[int, bool] def reset_module(self): """ diff --git a/mythril/analysis/modules/multiple_sends.py b/mythril/analysis/modules/multiple_sends.py index 04a74bce..1e6881f5 100644 --- a/mythril/analysis/modules/multiple_sends.py +++ b/mythril/analysis/modules/multiple_sends.py @@ -1,6 +1,7 @@ """This module contains the detection code to find multiple sends occurring in a single transaction.""" from copy import copy +from typing import List from mythril.analysis.report import Issue from mythril.analysis.swc_data import MULTIPLE_SENDS @@ -14,7 +15,7 @@ log = logging.getLogger(__name__) class MultipleSendsAnnotation(StateAnnotation): - def __init__(self): + def __init__(self) -> None: self.calls = [] def __copy__(self): @@ -56,7 +57,7 @@ def _analyze_state(state: GlobalState): node = state.node instruction = state.get_current_instruction() - annotations = [a for a in state.get_annotations(MultipleSendsAnnotation)] + annotations = [a for a in state.get_annotations(MultipleSendsAnnotation)] # type: List[MultipleSendsAnnotation] if len(annotations) == 0: log.debug("Creating annotation for state") state.annotate(MultipleSendsAnnotation()) diff --git a/mythril/analysis/modules/unchecked_retval.py b/mythril/analysis/modules/unchecked_retval.py index 70d7f0fd..8034b4e5 100644 --- a/mythril/analysis/modules/unchecked_retval.py +++ b/mythril/analysis/modules/unchecked_retval.py @@ -1,12 +1,15 @@ """This module contains detection code to find occurrences of calls whose return value remains unchecked.""" from copy import copy +from typing import Dict, List from mythril.analysis import solver from mythril.analysis.report import Issue from mythril.analysis.swc_data import UNCHECKED_RET_VAL from mythril.analysis.modules.base import DetectionModule from mythril.exceptions import UnsatError +from mythril.laser.smt.bitvec import BitVec + from mythril.laser.ethereum.state.annotation import StateAnnotation from mythril.laser.ethereum.state.global_state import GlobalState @@ -16,8 +19,8 @@ log = logging.getLogger(__name__) class UncheckedRetvalAnnotation(StateAnnotation): - def __init__(self): - self.retvals = [] + def __init__(self) -> None: + self.retvals = [] # type: List[Dict[str, Union[int, BitVec]]] def __copy__(self): result = UncheckedRetvalAnnotation() @@ -60,7 +63,7 @@ def _analyze_state(state: GlobalState) -> list: instruction = state.get_current_instruction() node = state.node - annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)] + annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)] # type: List[UncheckedRetvalAnnotation] if len(annotations) == 0: state.annotate(UncheckedRetvalAnnotation()) annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)] diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index e99e60cd..49957a60 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -183,7 +183,7 @@ def get_call_data( memory_size - global_state.environment.calldata.calldatasize == 0 ) - if uses_entire_calldata == True: + if uses_entire_calldata is True: return global_state.environment.calldata try: diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 2889c92d..4aef4489 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -1281,11 +1281,11 @@ class Instruction: if len(results) > 0: return results - return self._sload_helper(global_state, int(index)) + return self._sload_helper(global_state, str(index)) @staticmethod def _sload_helper( - global_state: GlobalState, index: int, constraints=None + global_state: GlobalState, index: Union[str, int], constraints=None ): """ @@ -1295,10 +1295,10 @@ class Instruction: :return: """ try: - data = global_state.environment.active_account.storage[index] + data = global_state.environment.active_account.storage[str(index)] except KeyError: data = global_state.new_bitvec("storage_" + str(index), 256) - global_state.environment.active_account.storage[index] = data + global_state.environment.active_account.storage[str(index)] = data if constraints is not None: global_state.mstate.constraints += constraints diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index cca03a09..f4f8a979 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -24,7 +24,7 @@ class Storage: self.dynld = dynamic_loader self.address = address - def __getitem__(self, item: Union[int, slice]) -> Any: + def __getitem__(self, item: Union[str, int, slice]) -> Any: try: return self._storage[item] except KeyError: diff --git a/mythril/laser/ethereum/state/environment.py b/mythril/laser/ethereum/state/environment.py index 6fb018ab..69830007 100644 --- a/mythril/laser/ethereum/state/environment.py +++ b/mythril/laser/ethereum/state/environment.py @@ -22,7 +22,7 @@ class Environment: callvalue: ExprRef, origin: ExprRef, code=None, - ): + ) -> None: """ :param active_account: diff --git a/mythril/laser/ethereum/state/global_state.py b/mythril/laser/ethereum/state/global_state.py index 8facc663..2730fffc 100644 --- a/mythril/laser/ethereum/state/global_state.py +++ b/mythril/laser/ethereum/state/global_state.py @@ -1,5 +1,5 @@ """This module contains a representation of the global execution state.""" -from typing import Dict, Union, List, Iterable +from typing import Dict, Union, List, Iterable, TYPE_CHECKING from copy import copy, deepcopy from z3 import BitVec @@ -10,6 +10,10 @@ from mythril.laser.ethereum.state.environment import Environment from mythril.laser.ethereum.state.machine_state import MachineState from mythril.laser.ethereum.state.annotation import StateAnnotation +if TYPE_CHECKING: + from mythril.laser.ethereum.state.world_state import WorldState + from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction, ContractCreationTransaction + class GlobalState: """GlobalState represents the current globalstate.""" @@ -23,7 +27,7 @@ class GlobalState: transaction_stack=None, last_return_data=None, annotations=None, - ): + ) -> None: """Constructor for GlobalState. :param world_state: diff --git a/mythril/laser/ethereum/state/world_state.py b/mythril/laser/ethereum/state/world_state.py index 7bcd06b4..dcd61f9b 100644 --- a/mythril/laser/ethereum/state/world_state.py +++ b/mythril/laser/ethereum/state/world_state.py @@ -1,7 +1,7 @@ """This module contains a representation of the EVM's world state.""" from copy import copy from random import randint -from typing import List, Iterator +from typing import Dict, List, Iterator from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.annotation import StateAnnotation @@ -19,7 +19,7 @@ class WorldState: :param transaction_sequence: :param annotations: """ - self.accounts = {} + self.accounts = {} # type: Dict[str, Account] self.node = None self.transaction_sequence = transaction_sequence or [] self._annotations = annotations or [] diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 31bcfbc0..281a73b0 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -110,7 +110,7 @@ def get_concrete_int(item: Union[int, Expression]) -> int: def concrete_int_from_bytes( - concrete_bytes: List[Union[BitVec, int]], start_index: int + concrete_bytes: Union[List[Union[BitVec, int]], bytes], start_index: int ) -> int: """ From 70c5d290272b3a49f2d7a736e5d7da1b3a04bfcc Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 23:17:06 +0530 Subject: [PATCH 15/42] Add more type hints from analysis modules --- .../modules/dependence_on_predictable_vars.py | 5 ++--- mythril/analysis/modules/integer.py | 4 +++- mythril/analysis/modules/multiple_sends.py | 15 +++++++++++---- mythril/analysis/modules/unchecked_retval.py | 12 +++++++++--- mythril/laser/ethereum/state/calldata.py | 6 +++--- mythril/laser/ethereum/state/global_state.py | 5 ++++- .../ethereum/transaction/transaction_models.py | 12 ++++++------ 7 files changed, 38 insertions(+), 21 deletions(-) diff --git a/mythril/analysis/modules/dependence_on_predictable_vars.py b/mythril/analysis/modules/dependence_on_predictable_vars.py index fc965307..1450f93a 100644 --- a/mythril/analysis/modules/dependence_on_predictable_vars.py +++ b/mythril/analysis/modules/dependence_on_predictable_vars.py @@ -118,10 +118,9 @@ def _analyze_states(state: GlobalState) -> list: m = re.search(r"blockhash\w+(\s-\s(\d+))*", str(constraint)) if m and solve(call): - found = m.group(1) - print(type(found)) + found_item = m.group(1) - if found: # block.blockhash(block.number - N) + if found_item: # block.blockhash(block.number - N) description = ( "The predictable expression 'block.blockhash(block.number - " + m.group(2) diff --git a/mythril/analysis/modules/integer.py b/mythril/analysis/modules/integer.py index ae0a12d3..6cbfd5ac 100644 --- a/mythril/analysis/modules/integer.py +++ b/mythril/analysis/modules/integer.py @@ -27,7 +27,9 @@ log = logging.getLogger(__name__) class OverUnderflowAnnotation: - def __init__(self, overflowing_state: GlobalState, operator: str, constraint) -> None: + def __init__( + self, overflowing_state: GlobalState, operator: str, constraint + ) -> None: self.overflowing_state = overflowing_state self.operator = operator self.constraint = constraint diff --git a/mythril/analysis/modules/multiple_sends.py b/mythril/analysis/modules/multiple_sends.py index 1e6881f5..e7ae0055 100644 --- a/mythril/analysis/modules/multiple_sends.py +++ b/mythril/analysis/modules/multiple_sends.py @@ -1,8 +1,9 @@ """This module contains the detection code to find multiple sends occurring in a single transaction.""" from copy import copy -from typing import List +from typing import cast, List, Union +from mythril.analysis.ops import Call from mythril.analysis.report import Issue from mythril.analysis.swc_data import MULTIPLE_SENDS from mythril.analysis.modules.base import DetectionModule @@ -16,7 +17,7 @@ log = logging.getLogger(__name__) class MultipleSendsAnnotation(StateAnnotation): def __init__(self) -> None: - self.calls = [] + self.calls = [] # type: List[Union[Call, None]] def __copy__(self): result = MultipleSendsAnnotation() @@ -57,11 +58,17 @@ def _analyze_state(state: GlobalState): node = state.node instruction = state.get_current_instruction() - annotations = [a for a in state.get_annotations(MultipleSendsAnnotation)] # type: List[MultipleSendsAnnotation] + annotations = cast( + List[MultipleSendsAnnotation], + [a for a in state.get_annotations(MultipleSendsAnnotation)], + ) if len(annotations) == 0: log.debug("Creating annotation for state") state.annotate(MultipleSendsAnnotation()) - annotations = [a for a in state.get_annotations(MultipleSendsAnnotation)] + annotations = cast( + List[MultipleSendsAnnotation], + [a for a in state.get_annotations(MultipleSendsAnnotation)], + ) calls = annotations[0].calls diff --git a/mythril/analysis/modules/unchecked_retval.py b/mythril/analysis/modules/unchecked_retval.py index 8034b4e5..ba05bb96 100644 --- a/mythril/analysis/modules/unchecked_retval.py +++ b/mythril/analysis/modules/unchecked_retval.py @@ -1,7 +1,7 @@ """This module contains detection code to find occurrences of calls whose return value remains unchecked.""" from copy import copy -from typing import Dict, List +from typing import cast, Dict, List, Union from mythril.analysis import solver from mythril.analysis.report import Issue @@ -63,10 +63,16 @@ def _analyze_state(state: GlobalState) -> list: instruction = state.get_current_instruction() node = state.node - annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)] # type: List[UncheckedRetvalAnnotation] + annotations = cast( + List[UncheckedRetvalAnnotation], + [a for a in state.get_annotations(UncheckedRetvalAnnotation)], + ) if len(annotations) == 0: state.annotate(UncheckedRetvalAnnotation()) - annotations = [a for a in state.get_annotations(UncheckedRetvalAnnotation)] + annotations = cast( + List[UncheckedRetvalAnnotation], + [a for a in state.get_annotations(UncheckedRetvalAnnotation)], + ) retvals = annotations[0].retvals diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index b2ea15a0..3901a158 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -114,7 +114,7 @@ class BaseCalldata: class ConcreteCalldata(BaseCalldata): """A concrete call data representation.""" - def __init__(self, tx_id: int, calldata: list): + def __init__(self, tx_id: str, calldata: list): """Initializes the ConcreteCalldata object. :param tx_id: Id of the transaction that the calldata is for. @@ -161,7 +161,7 @@ class ConcreteCalldata(BaseCalldata): class BasicConcreteCalldata(BaseCalldata): """A base class to represent concrete call data.""" - def __init__(self, tx_id: int, calldata: list): + def __init__(self, tx_id: str, calldata: list): """Initializes the ConcreteCalldata object, that doesn't use z3 arrays. :param tx_id: Id of the transaction that the calldata is for. @@ -207,7 +207,7 @@ class BasicConcreteCalldata(BaseCalldata): class SymbolicCalldata(BaseCalldata): """A class for representing symbolic call data.""" - def __init__(self, tx_id: int): + def __init__(self, tx_id: str): """Initializes the SymbolicCalldata object. :param tx_id: Id of the transaction that the calldata is for. diff --git a/mythril/laser/ethereum/state/global_state.py b/mythril/laser/ethereum/state/global_state.py index 2730fffc..aa931606 100644 --- a/mythril/laser/ethereum/state/global_state.py +++ b/mythril/laser/ethereum/state/global_state.py @@ -12,7 +12,10 @@ from mythril.laser.ethereum.state.annotation import StateAnnotation if TYPE_CHECKING: from mythril.laser.ethereum.state.world_state import WorldState - from mythril.laser.ethereum.transaction.transaction_models import MessageCallTransaction, ContractCreationTransaction + from mythril.laser.ethereum.transaction.transaction_models import ( + MessageCallTransaction, + ContractCreationTransaction, + ) class GlobalState: diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index a915c0e3..c22ba5a6 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -30,7 +30,7 @@ def get_next_transaction_id() -> int: class TransactionEndSignal(Exception): """Exception raised when a transaction is finalized.""" - def __init__(self, global_state: GlobalState, revert=False): + def __init__(self, global_state: GlobalState, revert=False) -> None: self.global_state = global_state self.revert = revert @@ -42,7 +42,7 @@ class TransactionStartSignal(Exception): self, transaction: Union["MessageCallTransaction", "ContractCreationTransaction"], op_code: str, - ): + ) -> None: self.transaction = transaction self.op_code = op_code @@ -63,7 +63,7 @@ class BaseTransaction: code=None, call_value=None, init_call_data=True, - ): + ) -> None: assert isinstance(world_state, WorldState) self.world_state = world_state self.id = identifier or get_next_transaction_id() @@ -99,7 +99,7 @@ class BaseTransaction: else symbol_factory.BitVecSym("callvalue{}".format(identifier), 256) ) - self.return_data = None + self.return_data = None # type: str def initial_global_state_from_environment(self, environment, active_function): """ @@ -117,7 +117,7 @@ class BaseTransaction: class MessageCallTransaction(BaseTransaction): """Transaction object models an transaction.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs) def initial_global_state(self) -> GlobalState: @@ -149,7 +149,7 @@ class MessageCallTransaction(BaseTransaction): class ContractCreationTransaction(BaseTransaction): """Transaction object models an transaction.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: super().__init__(*args, **kwargs, init_call_data=False) # TODO: set correct balance for new account self.callee_account = self.callee_account or self.world_state.create_account( From 5a0940bd102d4268a4ee44a6676e45632b8da538 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 23:21:26 +0530 Subject: [PATCH 16/42] Remove print in signatures file --- mythril/support/signatures.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index 1e749fbe..ff76be36 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -60,7 +60,6 @@ class Singleton(type): """ if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) - print(type(cls), cls._instances[cls]) return cls._instances[cls] From e8b480082482c433649951b01c14445b1077f16b Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 23:24:38 +0530 Subject: [PATCH 17/42] Remove extra str's --- mythril/laser/ethereum/instructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 4aef4489..f1e1c999 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -1295,10 +1295,10 @@ class Instruction: :return: """ try: - data = global_state.environment.active_account.storage[str(index)] + data = global_state.environment.active_account.storage[index] except KeyError: data = global_state.new_bitvec("storage_" + str(index), 256) - global_state.environment.active_account.storage[str(index)] = data + global_state.environment.active_account.storage[index] = data if constraints is not None: global_state.mstate.constraints += constraints From 0f61e60a09afc9d30cec6df5f867af4f7ff2ed89 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Thu, 17 Jan 2019 23:48:15 +0530 Subject: [PATCH 18/42] Fix all type hinting errors in native contracts --- mythril/analysis/modules/multiple_sends.py | 4 +-- mythril/laser/ethereum/natives.py | 36 ++++++++++------------ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/mythril/analysis/modules/multiple_sends.py b/mythril/analysis/modules/multiple_sends.py index e7ae0055..fbe0c03b 100644 --- a/mythril/analysis/modules/multiple_sends.py +++ b/mythril/analysis/modules/multiple_sends.py @@ -1,7 +1,7 @@ """This module contains the detection code to find multiple sends occurring in a single transaction.""" from copy import copy -from typing import cast, List, Union +from typing import cast, List, Optional from mythril.analysis.ops import Call from mythril.analysis.report import Issue @@ -17,7 +17,7 @@ log = logging.getLogger(__name__) class MultipleSendsAnnotation(StateAnnotation): def __init__(self) -> None: - self.calls = [] # type: List[Union[Call, None]] + self.calls = [] # type: List[Optional[Call]] def __copy__(self): result = MultipleSendsAnnotation() diff --git a/mythril/laser/ethereum/natives.py b/mythril/laser/ethereum/natives.py index fd801288..ef38d8c9 100644 --- a/mythril/laser/ethereum/natives.py +++ b/mythril/laser/ethereum/natives.py @@ -59,23 +59,23 @@ def ecrecover(data: List[int]) -> List[int]: """ # TODO: Add type hints try: - data = bytearray(data) - v = extract32(data, 32) - r = extract32(data, 64) - s = extract32(data, 96) + byte_data = bytearray(data) + v = extract32(byte_data, 32) + r = extract32(byte_data, 64) + s = extract32(byte_data, 96) except TypeError: raise NativeContractException - message = b"".join([ALL_BYTES[x] for x in data[0:32]]) + message = b"".join([ALL_BYTES[x] for x in byte_data[0:32]]) if r >= secp256k1n or s >= secp256k1n or v < 27 or v > 28: return [] try: pub = ecrecover_to_pub(message, v, r, s) except Exception as e: - log.debug("An error has occured while extracting public key: " + e) + log.debug("An error has occured while extracting public key: " + str(e)) return [] o = [0] * 12 + [x for x in sha3(pub)[-20:]] - return o + return list(bytearray(o)) def sha256(data: List[int]) -> List[int]: @@ -85,25 +85,25 @@ def sha256(data: List[int]) -> List[int]: :return: """ try: - data = bytes(data) + byte_data = bytes(data) except TypeError: raise NativeContractException - return hashlib.sha256(data).digest() + return list(bytearray(hashlib.sha256(byte_data).digest())) -def ripemd160(data: List[int]) -> bytes: +def ripemd160(data: List[int]) -> List[int]: """ :param data: :return: """ try: - data = bytes(data) + bytes_data = bytes(data) except TypeError: raise NativeContractException - digest = hashlib.new("ripemd160", data).digest() + digest = hashlib.new("ripemd160", bytes_data).digest() padded = 12 * [0] + list(digest) - return bytes(padded) + return list(bytearray(bytes(padded))) def identity(data: List[int]) -> List[int]: @@ -118,13 +118,9 @@ def identity(data: List[int]) -> List[int]: # implementation would be byte indexed for the most # part. return data - result = [] - for i in range(0, len(data), 32): - result.append(simplify(Concat(data[i : i + 32]))) - return result -def native_contracts(address: int, data: BaseCalldata): +def native_contracts(address: int, data: BaseCalldata) -> List[int]: """Takes integer address 1, 2, 3, 4. :param address: @@ -134,8 +130,8 @@ def native_contracts(address: int, data: BaseCalldata): functions = (ecrecover, sha256, ripemd160, identity) if isinstance(data, ConcreteCalldata): - data = data.concrete(None) + concrete_data = data.concrete(None) else: raise NativeContractException() - return functions[address - 1](data) + return functions[address - 1](concrete_data) From eb5850c6431ed4700d0dfcab94383c45529f0a17 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 18 Jan 2019 00:15:51 +0530 Subject: [PATCH 19/42] Type hint more modules --- mythril/laser/ethereum/instructions.py | 2 +- mythril/laser/ethereum/state/account.py | 4 +-- mythril/laser/ethereum/state/calldata.py | 10 +++---- mythril/laser/ethereum/state/memory.py | 33 +++++++++++++++++------- mythril/laser/ethereum/util.py | 5 +++- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index f1e1c999..e1f14ea3 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -1253,7 +1253,7 @@ class Instruction: except TypeError: if not keccak_function_manager.is_keccak(index): - return self._sload_helper(global_state, index) + return self._sload_helper(global_state, str(index)) storage_keys = global_state.environment.active_account.storage.keys() keccak_keys = list(filter(keccak_function_manager.is_keccak, storage_keys)) diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index f4f8a979..bd4b5204 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -14,7 +14,7 @@ from mythril.laser.smt import symbol_factory class Storage: """Storage class represents the storage of an Account.""" - def __init__(self, concrete=False, address=None, dynamic_loader=None): + def __init__(self, concrete=False, address=None, dynamic_loader=None) -> None: """Constructor for Storage. :param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic @@ -73,7 +73,7 @@ class Account: balance=None, concrete_storage=False, dynamic_loader=None, - ): + ) -> None: """Constructor for account. :param address: Address of the account diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 3901a158..46ed1c3e 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -26,7 +26,7 @@ class BaseCalldata: """Base calldata class This represents the calldata provided when sending a transaction to a contract.""" - def __init__(self, tx_id): + def __init__(self, tx_id: str) -> None: """ :param tx_id: @@ -114,7 +114,7 @@ class BaseCalldata: class ConcreteCalldata(BaseCalldata): """A concrete call data representation.""" - def __init__(self, tx_id: str, calldata: list): + def __init__(self, tx_id: str, calldata: list) -> None: """Initializes the ConcreteCalldata object. :param tx_id: Id of the transaction that the calldata is for. @@ -161,7 +161,7 @@ class ConcreteCalldata(BaseCalldata): class BasicConcreteCalldata(BaseCalldata): """A base class to represent concrete call data.""" - def __init__(self, tx_id: str, calldata: list): + def __init__(self, tx_id: str, calldata: list) -> None: """Initializes the ConcreteCalldata object, that doesn't use z3 arrays. :param tx_id: Id of the transaction that the calldata is for. @@ -207,7 +207,7 @@ class BasicConcreteCalldata(BaseCalldata): class SymbolicCalldata(BaseCalldata): """A class for representing symbolic call data.""" - def __init__(self, tx_id: str): + def __init__(self, tx_id: str) -> None: """Initializes the SymbolicCalldata object. :param tx_id: Id of the transaction that the calldata is for. @@ -258,7 +258,7 @@ class SymbolicCalldata(BaseCalldata): class BasicSymbolicCalldata(BaseCalldata): """A basic class representing symbolic call data.""" - def __init__(self, tx_id: int): + def __init__(self, tx_id: str) -> None: """Initializes the SymbolicCalldata object. :param tx_id: Id of the transaction that the calldata is for. diff --git a/mythril/laser/ethereum/state/memory.py b/mythril/laser/ethereum/state/memory.py index 64be50f3..2a658304 100644 --- a/mythril/laser/ethereum/state/memory.py +++ b/mythril/laser/ethereum/state/memory.py @@ -1,5 +1,5 @@ """This module contains a representation of a smart contract's memory.""" -from typing import Union +from typing import cast, List, Union from z3 import Z3Exception @@ -45,17 +45,24 @@ class Memory: try: return symbol_factory.BitVecVal( util.concrete_int_from_bytes( - bytes([util.get_concrete_int(b) for b in self[index : index + 32]]), + bytes( + [ + util.get_concrete_int(b) + for b in self[index : index + 32] # type: ignore + ] + ), 0, ), 256, ) - except: + except TypeError: result = simplify( Concat( [ b if isinstance(b, BitVec) else symbol_factory.BitVecVal(b, 8) - for b in self[index : index + 32] + for b in cast( + List[Union[int, BitVec]], self[index : index + 32] + ) ] ) ) @@ -79,7 +86,7 @@ class Memory: else: _bytes = util.concrete_int_to_bytes(value) assert len(_bytes) == 32 - self[index : index + 32] = _bytes + self[index : index + 32] = list(bytearray(_bytes)) except (Z3Exception, AttributeError): # BitVector or BoolRef if isinstance(value, Bool): value_to_write = If( @@ -94,7 +101,9 @@ class Memory: for i in range(0, value_to_write.size(), 8): self[index + 31 - (i // 8)] = Extract(i + 7, i, value_to_write) - def __getitem__(self, item: Union[int, slice]) -> Union[BitVec, int, list]: + def __getitem__( + self, item: Union[int, slice] + ) -> Union[BitVec, int, List[Union[int, BitVec]]]: """ :param item: @@ -108,14 +117,18 @@ class Memory: raise IndexError("Invalid Memory Slice") if step is None: step = 1 - return [self[i] for i in range(start, stop, step)] + return [cast(Union[int, BitVec], self[i]) for i in range(start, stop, step)] try: return self._memory[item] except IndexError: return 0 - def __setitem__(self, key: Union[int, slice], value: Union[BitVec, int, list]): + def __setitem__( + self, + key: Union[int, slice], + value: Union[BitVec, int, List[Union[int, BitVec]]], + ): """ :param key: @@ -130,9 +143,9 @@ class Memory: raise IndexError("Invalid Memory Slice") if step is None: step = 1 - + assert type(value) == list for i in range(0, stop - start, step): - self[start + i] = value[i] + self[start + i] = cast(List[Union[int, BitVec]], value)[i] else: if isinstance(value, int): diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 281a73b0..ab81d956 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -108,6 +108,8 @@ def get_concrete_int(item: Union[int, Expression]) -> int: raise TypeError("Symbolic boolref encountered") return value + assert False, "Unhandled type {} encountered".format(str(type(item))) + def concrete_int_from_bytes( concrete_bytes: Union[List[Union[BitVec, int]], bytes], start_index: int @@ -124,7 +126,8 @@ def concrete_int_from_bytes( ] integer_bytes = concrete_bytes[start_index : start_index + 32] - return int.from_bytes(integer_bytes, byteorder="big") + # The below statement is expected to fail in some circumstances + return int.from_bytes(integer_bytes, byteorder="big") # type: ignore def concrete_int_to_bytes(val): From d35bb7892f2ebde220103b105d49bf6ec8a5d5e6 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 18 Jan 2019 00:30:53 +0530 Subject: [PATCH 20/42] Reduce type hint errors to 48 --- mythril/disassembler/disassembly.py | 4 ++-- mythril/support/signatures.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mythril/disassembler/disassembly.py b/mythril/disassembler/disassembly.py index 1d5bb601..0cc1c8aa 100644 --- a/mythril/disassembler/disassembly.py +++ b/mythril/disassembler/disassembly.py @@ -3,7 +3,7 @@ from mythril.ethereum import util from mythril.disassembler import asm from mythril.support.signatures import SignatureDB -from typing import Dict, List +from typing import Dict, List, Tuple class Disassembly(object): @@ -57,7 +57,7 @@ class Disassembly(object): def get_function_info( index: int, instruction_list: list, signature_database: SignatureDB -) -> (str, int, str): +) -> Tuple[str, int, str]: """Finds the function information for a call table entry Solidity uses the first 4 bytes of the calldata to indicate which function the message call should execute The generated code that directs execution to the correct diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index ff76be36..3a113d99 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -7,7 +7,7 @@ import sqlite3 import time from collections import defaultdict from subprocess import PIPE, Popen -from typing import List, Dict, Set +from typing import List, Set, DefaultDict from mythril.exceptions import CompilerError @@ -121,12 +121,12 @@ class SignatureDB(object, metaclass=Singleton): :param path: """ self.enable_online_lookup = enable_online_lookup - self.online_lookup_miss = set() + self.online_lookup_miss = set() # type: Set[str] self.online_lookup_timeout = 0 # if we're analysing a Solidity file, store its hashes # here to prevent unnecessary lookups - self.solidity_sigs = defaultdict(list) + self.solidity_sigs = defaultdict(list) # type: DefaultDict[str, List[str]] if path is None: self.path = os.environ.get("MYTHRIL_DIR") or os.path.join( os.path.expanduser("~"), ".mythril" @@ -226,7 +226,7 @@ class SignatureDB(object, metaclass=Singleton): return text_sigs except FourByteDirectoryOnlineLookupError as fbdole: # wait at least 2 mins to try again - self.online_lookup_timeout = time.time() + 2 * 60 + self.online_lookup_timeout = int(time.time()) + 2 * 60 log.warning("Online lookup failed, not retrying for 2min: %s", fbdole) return [] From b2b21431cd255c33fed711aee2d1d749b3968474 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 18 Jan 2019 13:46:05 +0530 Subject: [PATCH 21/42] Fix all mypy issues in instructions module --- mythril/laser/ethereum/call.py | 32 +++++++++++-------- mythril/laser/ethereum/instructions.py | 31 +++++++++++------- mythril/laser/ethereum/state/account.py | 9 +++--- mythril/laser/ethereum/state/calldata.py | 27 ++++++++-------- mythril/laser/ethereum/state/machine_state.py | 14 ++++---- mythril/laser/ethereum/state/memory.py | 4 +-- mythril/laser/ethereum/state/world_state.py | 7 ++-- mythril/support/loader.py | 2 +- 8 files changed, 73 insertions(+), 53 deletions(-) diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py index 49957a60..68ec6c3b 100644 --- a/mythril/laser/ethereum/call.py +++ b/mythril/laser/ethereum/call.py @@ -3,9 +3,9 @@ instructions.py to get the necessary elements from the stack and determine the parameters for the new global state.""" import logging -from typing import Union, List +from typing import Union, List, cast, Callable from z3 import Z3Exception - +from mythril.laser.smt import BitVec from mythril.laser.ethereum import natives from mythril.laser.ethereum.gas import OPCODE_GAS from mythril.laser.smt import simplify, Expression, symbol_factory @@ -155,8 +155,8 @@ def get_callee_account( def get_call_data( global_state: GlobalState, - memory_start: Union[int, Expression], - memory_size: Union[int, Expression], + memory_start: Union[int, BitVec], + memory_size: Union[int, BitVec], ): """Gets call_data from the global_state. @@ -168,15 +168,21 @@ def get_call_data( state = global_state.mstate transaction_id = "{}_internalcall".format(global_state.current_transaction.id) - memory_start = ( - symbol_factory.BitVecVal(memory_start, 256) - if isinstance(memory_start, int) - else memory_start + memory_start = cast( + BitVec, + ( + symbol_factory.BitVecVal(memory_start, 256) + if isinstance(memory_start, int) + else memory_start + ), ) - memory_size = ( - symbol_factory.BitVecVal(memory_size, 256) - if isinstance(memory_size, int) - else memory_size + memory_size = cast( + BitVec, + ( + symbol_factory.BitVecVal(memory_size, 256) + if isinstance(memory_size, int) + else memory_size + ), ) uses_entire_calldata = simplify( @@ -218,7 +224,7 @@ def native_call( contract_list = ["ecrecover", "sha256", "ripemd160", "identity"] call_address_int = int(callee_address, 16) - native_gas_min, native_gas_max = OPCODE_GAS["NATIVE_COST"]( + native_gas_min, native_gas_max = cast(Callable, OPCODE_GAS["NATIVE_COST"])( global_state.mstate.calculate_extension_size(mem_out_start, mem_out_sz), contract_list[call_address_int - 1], ) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index e1f14ea3..77a88e94 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -778,7 +778,9 @@ class Instruction: new_memory.append(value) i_data = ( - i_data + 1 if isinstance(i_data, int) else simplify(i_data + 1) + i_data + 1 + if isinstance(i_data, int) + else simplify(cast(BitVec, i_data) + 1) ) for i in range(len(new_memory)): state.memory[i + mstart] = new_memory[i] @@ -881,11 +883,12 @@ class Instruction: state.stack.append( symbol_factory.BitVecSym("KECCAC_mem[" + str(op0) + "]", 256) ) - state.min_gas_used += OPCODE_GAS["SHA3"][0] - state.max_gas_used += OPCODE_GAS["SHA3"][1] + gas_tuple = cast(Tuple, OPCODE_GAS["SHA3"]) + state.min_gas_used += gas_tuple[0] + state.max_gas_used += gas_tuple[1] return [global_state] - min_gas, max_gas = OPCODE_GAS["SHA3_FUNC"](length) + min_gas, max_gas = cast(Callable, OPCODE_GAS["SHA3_FUNC"])(length) state.min_gas_used += min_gas state.max_gas_used += max_gas StateTransition.check_gas_usage_limit(global_state) @@ -1225,7 +1228,9 @@ class Instruction: state.mem_extend(offset, 1) try: - value_to_write = util.get_concrete_int(value) ^ 0xFF + value_to_write = ( + util.get_concrete_int(value) ^ 0xFF + ) # type: Union[int, BitVec] except TypeError: # BitVec value_to_write = Extract(7, 0, value) log.debug("MSTORE8 to mem[" + str(offset) + "]: " + str(value_to_write)) @@ -1348,13 +1353,17 @@ class Instruction: new = symbol_factory.Bool(False) for keccak_key in keccak_keys: - key_argument = keccak_function_manager.get_argument(keccak_key) - index_argument = keccak_function_manager.get_argument(index) + key_argument = keccak_function_manager.get_argument( + keccak_key + ) # type: Expression + index_argument = keccak_function_manager.get_argument( + index + ) # type: Expression condition = key_argument == index_argument condition = ( condition if type(condition) == bool - else is_true(simplify(condition)) + else is_true(simplify(cast(Bool, condition))) ) if condition: return self._sstore_helper( @@ -1371,7 +1380,7 @@ class Instruction: key_argument == index_argument, ) - new = Or(new, key_argument != index_argument) + new = Or(new, cast(Bool, key_argument != index_argument)) if len(results) > 0: results += self._sstore_helper( @@ -1439,7 +1448,7 @@ class Instruction: new_state = copy(global_state) # add JUMP gas cost - min_gas, max_gas = OPCODE_GAS["JUMP"] + min_gas, max_gas = cast(Tuple[int, int], OPCODE_GAS["JUMP"]) new_state.mstate.min_gas_used += min_gas new_state.mstate.max_gas_used += max_gas @@ -1458,7 +1467,7 @@ class Instruction: """ state = global_state.mstate disassembly = global_state.environment.code - min_gas, max_gas = OPCODE_GAS["JUMPI"] + min_gas, max_gas = cast(Tuple[int, int], OPCODE_GAS["JUMPI"]) states = [] op0, condition = state.stack.pop(), state.stack.pop() diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index bd4b5204..1d3b02de 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -19,12 +19,12 @@ class Storage: :param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic """ - self._storage = {} + self._storage = {} # type: Dict[Union[int, str], Any] self.concrete = concrete self.dynld = dynamic_loader self.address = address - def __getitem__(self, item: Union[str, int, slice]) -> Any: + def __getitem__(self, item: Union[str, int]) -> Any: try: return self._storage[item] except KeyError: @@ -37,7 +37,8 @@ class Storage: self._storage[item] = symbol_factory.BitVecVal( int( self.dynld.read_storage( - contract_address=self.address, index=int(item) + contract_address=self.address, + index=int(item), # type: ignore ), 16, ), @@ -51,7 +52,7 @@ class Storage: self._storage[item] = symbol_factory.BitVecVal(0, 256) return self._storage[item] - def __setitem__(self, key: int, value: ExprRef) -> None: + def __setitem__(self, key: Union[int, str], value: Any) -> None: self._storage[key] = value def keys(self) -> KeysView: diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 46ed1c3e..8ddf3527 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -1,7 +1,6 @@ """This module declares classes to represent call data.""" -from typing import Union, Any +from typing import cast, Union, Tuple, List -from mythril.laser.smt import K, Array, If, simplify, Concat, Expression, BitVec from enum import Enum from typing import Any, Union @@ -88,7 +87,7 @@ class BaseCalldata: raise ValueError - def _load(self, item: Union[int, Expression]) -> Any: + def _load(self, item: Union[int, BitVec]) -> Any: """ :param item: @@ -96,7 +95,7 @@ class BaseCalldata: raise NotImplementedError() @property - def size(self) -> Union[Expression, int]: + def size(self) -> Union[BitVec, int]: """Returns the exact size of this calldata, this is not normalized. :return: unnormalized call data size @@ -132,13 +131,16 @@ class ConcreteCalldata(BaseCalldata): super().__init__(tx_id) - def _load(self, item: Union[int, Expression]) -> BitVec: + def _load(self, item: Union[int, BitVec]) -> BitVec: """ :param item: :return: """ - item = symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item + item = cast( + BitVec, + symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item, + ) return simplify(self._calldata[item]) def concrete(self, model: Model) -> list: @@ -216,7 +218,7 @@ class SymbolicCalldata(BaseCalldata): self._calldata = Array("{}_calldata".format(tx_id), 256, 8) super().__init__(tx_id) - def _load(self, item: Union[int, Expression]) -> Any: + def _load(self, item: Union[int, BitVec]) -> Any: """ :param item: @@ -226,7 +228,7 @@ class SymbolicCalldata(BaseCalldata): return simplify( If( item < self._size, - simplify(self._calldata[item]), + simplify(self._calldata[cast(BitVec, item)]), symbol_factory.BitVecVal(0, 8), ) ) @@ -247,7 +249,7 @@ class SymbolicCalldata(BaseCalldata): return result @property - def size(self) -> Expression: + def size(self) -> BitVec: """ :return: @@ -263,11 +265,11 @@ class BasicSymbolicCalldata(BaseCalldata): :param tx_id: Id of the transaction that the calldata is for. """ - self._reads = [] + self._reads = [] # type: List[Tuple[Union[int, BitVec], BitVec]] self._size = BitVec(str(tx_id) + "_calldatasize", 256) super().__init__(tx_id) - def _load(self, item: Union[int, Expression], clean=False) -> Any: + def _load(self, item: Union[int, BitVec], clean=False) -> Any: x = symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item symbolic_base_value = If( @@ -278,7 +280,6 @@ class BasicSymbolicCalldata(BaseCalldata): return_value = symbolic_base_value for r_index, r_value in self._reads: return_value = If(r_index == item, r_value, return_value) - if not clean: self._reads.append((item, symbolic_base_value)) return simplify(return_value) @@ -299,7 +300,7 @@ class BasicSymbolicCalldata(BaseCalldata): return result @property - def size(self) -> Expression: + def size(self) -> BitVec: """ :return: diff --git a/mythril/laser/ethereum/state/machine_state.py b/mythril/laser/ethereum/state/machine_state.py index 33e7bfe2..720183be 100644 --- a/mythril/laser/ethereum/state/machine_state.py +++ b/mythril/laser/ethereum/state/machine_state.py @@ -1,9 +1,9 @@ """This module contains a representation of the EVM's machine state and its stack.""" from copy import copy -from typing import Union, Any, List, Dict +from typing import cast, Sized, Union, Any, List, Dict, Optional -from z3 import BitVec +from mythril.laser.smt import Expression from ethereum import opcodes, utils from mythril.laser.ethereum.evm_exceptions import ( @@ -29,7 +29,7 @@ class MachineStack(list): default_list = [] super(MachineStack, self).__init__(default_list) - def append(self, element: BitVec) -> None: + def append(self, element: Expression) -> None: """ :param element: element to be appended to the list :function: appends the element to list if the size is less than STACK_LIMIT, else throws an error @@ -41,7 +41,7 @@ class MachineStack(list): ) super(MachineStack, self).append(element) - def pop(self, index=-1) -> BitVec: + def pop(self, index=-1) -> Expression: """ :param index:index to be popped, same as the list() class. :returns popped value @@ -90,7 +90,7 @@ class MachineState: gas_limit: int, pc=0, stack=None, - memory=None, + memory: Optional[Memory] = None, constraints=None, depth=0, max_gas_used=0, @@ -164,7 +164,7 @@ class MachineState: self.check_gas() self.memory.extend(m_extend) - def memory_write(self, offset: int, data: List[int]) -> None: + def memory_write(self, offset: int, data: List[Union[int, BitVec]]) -> None: """Writes data to memory starting at offset. :param offset: @@ -217,7 +217,7 @@ class MachineState: :return: """ - return len(self.memory) + return len(cast(Sized, self.memory)) @property def as_dict(self) -> Dict: diff --git a/mythril/laser/ethereum/state/memory.py b/mythril/laser/ethereum/state/memory.py index 2a658304..064ee727 100644 --- a/mythril/laser/ethereum/state/memory.py +++ b/mythril/laser/ethereum/state/memory.py @@ -20,7 +20,7 @@ class Memory: def __init__(self): """""" - self._memory = [] + self._memory = [] # type: List[Union[int, BitVec]] def __len__(self): """ @@ -152,4 +152,4 @@ class Memory: assert 0 <= value <= 0xFF if isinstance(value, BitVec): assert value.size() == 8 - self._memory[key] = value + self._memory[key] = cast(Union[int, BitVec], value) diff --git a/mythril/laser/ethereum/state/world_state.py b/mythril/laser/ethereum/state/world_state.py index dcd61f9b..9b5bdcaa 100644 --- a/mythril/laser/ethereum/state/world_state.py +++ b/mythril/laser/ethereum/state/world_state.py @@ -1,11 +1,14 @@ """This module contains a representation of the EVM's world state.""" from copy import copy from random import randint -from typing import Dict, List, Iterator +from typing import Dict, List, Iterator, Optional, TYPE_CHECKING from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.annotation import StateAnnotation +if TYPE_CHECKING: + from mythril.laser.ethereum.cfg import Node + class WorldState: """The WorldState class represents the world state as described in the @@ -20,7 +23,7 @@ class WorldState: :param annotations: """ self.accounts = {} # type: Dict[str, Account] - self.node = None + self.node = None # type: Optional['Node'] self.transaction_sequence = transaction_sequence or [] self._annotations = annotations or [] diff --git a/mythril/support/loader.py b/mythril/support/loader.py index d55bc185..6e434439 100644 --- a/mythril/support/loader.py +++ b/mythril/support/loader.py @@ -22,7 +22,7 @@ class DynLoader: self.contract_loading = contract_loading self.storage_loading = storage_loading - def read_storage(self, contract_address, index): + def read_storage(self, contract_address: str, index: int): """ :param contract_address: From 9131799e81c72abcbe342e86c569f53665d05502 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 18 Jan 2019 14:04:26 +0530 Subject: [PATCH 22/42] Fix type hints for trasaction_models module --- mythril/laser/ethereum/keccak.py | 2 +- mythril/laser/ethereum/state/machine_state.py | 2 +- .../laser/ethereum/transaction/transaction_models.py | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mythril/laser/ethereum/keccak.py b/mythril/laser/ethereum/keccak.py index 9d2aefdc..47a39ed1 100644 --- a/mythril/laser/ethereum/keccak.py +++ b/mythril/laser/ethereum/keccak.py @@ -18,7 +18,7 @@ class KeccakFunctionManager: """ return str(expression) in self.keccak_expression_mapping.keys() - def get_argument(self, expression: str) -> Expression: + def get_argument(self, expression: Expression) -> Expression: """ :param expression: diff --git a/mythril/laser/ethereum/state/machine_state.py b/mythril/laser/ethereum/state/machine_state.py index 720183be..1a905c52 100644 --- a/mythril/laser/ethereum/state/machine_state.py +++ b/mythril/laser/ethereum/state/machine_state.py @@ -3,7 +3,7 @@ stack.""" from copy import copy from typing import cast, Sized, Union, Any, List, Dict, Optional -from mythril.laser.smt import Expression +from mythril.laser.smt import BitVec, Expression from ethereum import opcodes, utils from mythril.laser.ethereum.evm_exceptions import ( diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index c22ba5a6..596dba1a 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -3,7 +3,7 @@ execution.""" import array from z3 import ExprRef -from typing import Union +from typing import Union, Optional from mythril.laser.ethereum.state.calldata import ConcreteCalldata from mythril.laser.ethereum.state.account import Account @@ -17,14 +17,14 @@ from mythril.laser.smt import symbol_factory _next_transaction_id = 0 -def get_next_transaction_id() -> int: +def get_next_transaction_id() -> str: """ :return: """ global _next_transaction_id _next_transaction_id += 1 - return _next_transaction_id + return str(_next_transaction_id) class TransactionEndSignal(Exception): @@ -56,7 +56,7 @@ class BaseTransaction: callee_account: Account = None, caller: ExprRef = None, call_data=None, - identifier=None, + identifier: Optional[str] = None, gas_price=None, gas_limit=None, origin=None, @@ -85,7 +85,9 @@ class BaseTransaction: self.caller = caller self.callee_account = callee_account if call_data is None and init_call_data: - self.call_data = SymbolicCalldata(self.id) + self.call_data = SymbolicCalldata( + self.id + ) # type: Union[SymbolicCalldata, ConcreteCalldata] else: self.call_data = ( call_data From cbd344b8e877c67f9bf7a09333632ac81f52f669 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sat, 19 Jan 2019 12:18:29 +0530 Subject: [PATCH 23/42] Reduce type hinting errors --- mythril/laser/ethereum/state/calldata.py | 13 ++++++++----- mythril/laser/ethereum/state/machine_state.py | 5 ++--- mythril/laser/ethereum/strategy/basic.py | 7 +++++-- mythril/laser/ethereum/util.py | 13 ++++--------- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 8ddf3527..e69714b7 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -12,6 +12,7 @@ from mythril.laser.ethereum.util import get_concrete_int from mythril.laser.smt import ( Array, BitVec, + Bool, Concat, Expression, If, @@ -186,7 +187,7 @@ class BasicConcreteCalldata(BaseCalldata): value = symbol_factory.BitVecVal(0x0, 8) for i in range(self.size): - value = If(item == i, self._calldata[i], value) + value = If(cast(Union[BitVec, Bool], item) == i, self._calldata[i], value) return value def concrete(self, model: Model) -> list: @@ -270,18 +271,20 @@ class BasicSymbolicCalldata(BaseCalldata): super().__init__(tx_id) def _load(self, item: Union[int, BitVec], clean=False) -> Any: - x = symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item + expr_item = ( + symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item + ) symbolic_base_value = If( - x >= self._size, + expr_item >= self._size, symbol_factory.BitVecVal(0, 8), BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8), ) return_value = symbolic_base_value for r_index, r_value in self._reads: - return_value = If(r_index == item, r_value, return_value) + return_value = If(r_index == expr_item, r_value, return_value) if not clean: - self._reads.append((item, symbolic_base_value)) + self._reads.append((expr_item, symbolic_base_value)) return simplify(return_value) def concrete(self, model: Model) -> list: diff --git a/mythril/laser/ethereum/state/machine_state.py b/mythril/laser/ethereum/state/machine_state.py index 1a905c52..b71fe59d 100644 --- a/mythril/laser/ethereum/state/machine_state.py +++ b/mythril/laser/ethereum/state/machine_state.py @@ -25,15 +25,14 @@ class MachineStack(list): :param default_list: """ - if default_list is None: - default_list = [] - super(MachineStack, self).__init__(default_list) + super(MachineStack, self).__init__(default_list or []) def append(self, element: Expression) -> None: """ :param element: element to be appended to the list :function: appends the element to list if the size is less than STACK_LIMIT, else throws an error """ + assert isinstance(element, Expression) if super(MachineStack, self).__len__() >= self.STACK_LIMIT: raise StackOverflowException( "Reached the EVM stack limit of {}, you can't append more " diff --git a/mythril/laser/ethereum/strategy/basic.py b/mythril/laser/ethereum/strategy/basic.py index 6627241c..5708667e 100644 --- a/mythril/laser/ethereum/strategy/basic.py +++ b/mythril/laser/ethereum/strategy/basic.py @@ -1,5 +1,6 @@ """This module implements basic symbolic execution search strategies.""" from random import randrange +from typing import List from mythril.laser.ethereum.state.global_state import GlobalState from . import BasicSearchStrategy @@ -13,7 +14,9 @@ except ImportError: from random import random from bisect import bisect - def choices(population, weights=None): + def choices( + population: List, weights: List[int] = None + ) -> List[int]: # type: ignore """Returns a random element out of the population based on weight. If the relative weights or cumulative weights are not specified, @@ -21,7 +24,7 @@ except ImportError: """ if weights is None: return [population[int(random() * len(population))]] - cum_weights = accumulate(weights) + cum_weights = list(accumulate(weights)) return [ population[ bisect(cum_weights, random() * cum_weights[-1], 0, len(population) - 1) diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index ab81d956..4a701eb2 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -1,7 +1,7 @@ """This module contains various utility conversion functions and constants for LASER.""" import re -from typing import Dict, List, Union, TYPE_CHECKING +from typing import Dict, List, Union, TYPE_CHECKING, cast if TYPE_CHECKING: from mythril.laser.ethereum.state.machine_state import MachineState @@ -77,15 +77,10 @@ def pop_bitvec(state: "MachineState") -> BitVec: if type(item) == Bool: return If( - item, symbol_factory.BitVecVal(1, 256), symbol_factory.BitVecVal(0, 256) + cast(Bool, item), + symbol_factory.BitVecVal(1, 256), + symbol_factory.BitVecVal(0, 256), ) - elif type(item) == bool: - if item: - return symbol_factory.BitVecVal(1, 256) - else: - return symbol_factory.BitVecVal(0, 256) - elif type(item) == int: - return symbol_factory.BitVecVal(item, 256) else: return simplify(item) From f01187aed73e1759cbaf41eaf2356e57dc8f034f Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Sat, 19 Jan 2019 12:28:01 +0530 Subject: [PATCH 24/42] Fix mypy conflicts in laser --- mythril/laser/ethereum/strategy/basic.py | 4 ++-- mythril/laser/ethereum/transaction/transaction_models.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mythril/laser/ethereum/strategy/basic.py b/mythril/laser/ethereum/strategy/basic.py index 5708667e..ceb36d81 100644 --- a/mythril/laser/ethereum/strategy/basic.py +++ b/mythril/laser/ethereum/strategy/basic.py @@ -14,9 +14,9 @@ except ImportError: from random import random from bisect import bisect - def choices( + def choices( # type: ignore population: List, weights: List[int] = None - ) -> List[int]: # type: ignore + ) -> List[int]: """Returns a random element out of the population based on weight. If the relative weights or cumulative weights are not specified, diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 596dba1a..6273922a 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -152,7 +152,7 @@ class ContractCreationTransaction(BaseTransaction): """Transaction object models an transaction.""" def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs, init_call_data=False) + super().__init__(*args, **kwargs, init_call_data=False) # type: ignore # TODO: set correct balance for new account self.callee_account = self.callee_account or self.world_state.create_account( 0, concrete_storage=True From 8c69821058b3ce4691ac15927bb8cccfe165d940 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 16:33:27 +0530 Subject: [PATCH 25/42] Try removing type ignores and add the github issue if not possible --- mythril/analysis/callgraph.py | 2 +- mythril/analysis/modules/unchecked_retval.py | 9 ++++++--- mythril/laser/ethereum/state/account.py | 1 + mythril/laser/ethereum/state/memory.py | 17 ++++++++++------- mythril/laser/ethereum/strategy/basic.py | 1 + .../ethereum/transaction/transaction_models.py | 7 ++++--- mythril/laser/ethereum/util.py | 2 +- 7 files changed, 24 insertions(+), 15 deletions(-) diff --git a/mythril/analysis/callgraph.py b/mythril/analysis/callgraph.py index b02a62fa..0314a27e 100644 --- a/mythril/analysis/callgraph.py +++ b/mythril/analysis/callgraph.py @@ -3,7 +3,7 @@ graphs.""" import re -from jinja2 import Environment, PackageLoader, select_autoescape +from jinja2 import Environment, PackageLoader, select_autoescape # type: ignore from z3 import Z3Exception from mythril.laser.ethereum.svm import NodeFlags diff --git a/mythril/analysis/modules/unchecked_retval.py b/mythril/analysis/modules/unchecked_retval.py index ba05bb96..830d006e 100644 --- a/mythril/analysis/modules/unchecked_retval.py +++ b/mythril/analysis/modules/unchecked_retval.py @@ -1,7 +1,7 @@ """This module contains detection code to find occurrences of calls whose return value remains unchecked.""" from copy import copy -from typing import cast, Dict, List, Union +from typing import cast, List, Union, Mapping from mythril.analysis import solver from mythril.analysis.report import Issue @@ -20,7 +20,7 @@ log = logging.getLogger(__name__) class UncheckedRetvalAnnotation(StateAnnotation): def __init__(self) -> None: - self.retvals = [] # type: List[Dict[str, Union[int, BitVec]]] + self.retvals = [] # type: List[Mapping[str, Union[int, BitVec]]] def __copy__(self): result = UncheckedRetvalAnnotation() @@ -112,7 +112,10 @@ def _analyze_state(state: GlobalState) -> list: "opcode" ] in ["CALL", "DELEGATECALL", "STATICCALL", "CALLCODE"] retval = state.mstate.stack[-1] - retvals.append({"address": state.instruction["address"] - 1, "retval": retval}) + # Use Typed Dict after release of mypy 0.670 and remove type ignore + retvals.append( + {"address": state.instruction["address"] - 1, "retval": retval} + ) # type: ignore return [] diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index 1d3b02de..cac97ecc 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -38,6 +38,7 @@ class Storage: int( self.dynld.read_storage( contract_address=self.address, + # The line below is expected to raise a ValueError index=int(item), # type: ignore ), 16, diff --git a/mythril/laser/ethereum/state/memory.py b/mythril/laser/ethereum/state/memory.py index 064ee727..affd118c 100644 --- a/mythril/laser/ethereum/state/memory.py +++ b/mythril/laser/ethereum/state/memory.py @@ -1,5 +1,5 @@ """This module contains a representation of a smart contract's memory.""" -from typing import cast, List, Union +from typing import cast, List, Union, overload from z3 import Z3Exception @@ -45,12 +45,7 @@ class Memory: try: return symbol_factory.BitVecVal( util.concrete_int_from_bytes( - bytes( - [ - util.get_concrete_int(b) - for b in self[index : index + 32] # type: ignore - ] - ), + bytes([util.get_concrete_int(b) for b in self[index : index + 32]]), 0, ), 256, @@ -101,6 +96,14 @@ class Memory: for i in range(0, value_to_write.size(), 8): self[index + 31 - (i // 8)] = Extract(i + 7, i, value_to_write) + @overload + def __getitem__(self, item: int) -> Union[int, BitVec]: + ... + + @overload + def __getitem__(self, item: slice) -> List[Union[int, BitVec]]: + ... + def __getitem__( self, item: Union[int, slice] ) -> Union[BitVec, int, List[Union[int, BitVec]]]: diff --git a/mythril/laser/ethereum/strategy/basic.py b/mythril/laser/ethereum/strategy/basic.py index ceb36d81..9e9eaf12 100644 --- a/mythril/laser/ethereum/strategy/basic.py +++ b/mythril/laser/ethereum/strategy/basic.py @@ -14,6 +14,7 @@ except ImportError: from random import random from bisect import bisect + # Remove ignore after this has been fixed: https://github.com/python/mypy/issues/1297 def choices( # type: ignore population: List, weights: List[int] = None ) -> List[int]: diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 6273922a..6deefac3 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -3,7 +3,7 @@ execution.""" import array from z3 import ExprRef -from typing import Union, Optional +from typing import Union, Optional, cast from mythril.laser.ethereum.state.calldata import ConcreteCalldata from mythril.laser.ethereum.state.account import Account @@ -90,7 +90,7 @@ class BaseTransaction: ) # type: Union[SymbolicCalldata, ConcreteCalldata] else: self.call_data = ( - call_data + cast(Union[SymbolicCalldata, ConcreteCalldata], call_data) if isinstance(call_data, BaseCalldata) else ConcreteCalldata(self.id, []) ) @@ -152,7 +152,8 @@ class ContractCreationTransaction(BaseTransaction): """Transaction object models an transaction.""" def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs, init_call_data=False) # type: ignore + # Remove ignore after https://github.com/python/mypy/issues/4335 is fixed + super().__init__(*args, **kwargs, init_call_data=False) # type: ignore # TODO: set correct balance for new account self.callee_account = self.callee_account or self.world_state.create_account( 0, concrete_storage=True diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 4a701eb2..5b594fef 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -121,7 +121,7 @@ def concrete_int_from_bytes( ] integer_bytes = concrete_bytes[start_index : start_index + 32] - # The below statement is expected to fail in some circumstances + # The below statement is expected to fail in some circumstances whose error is caught return int.from_bytes(integer_bytes, byteorder="big") # type: ignore From 23c408ca2dcf9a934defcfb29200c2df16f0e71d Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 16:53:34 +0530 Subject: [PATCH 26/42] Fix rest of the the mypy issues which aren't related to smt abstraction module --- mythril/analysis/modules/unchecked_retval.py | 7 +++++-- mythril/laser/ethereum/state/machine_state.py | 5 ++--- mythril/laser/ethereum/util.py | 2 ++ mythril/support/signatures.py | 4 ++-- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/mythril/analysis/modules/unchecked_retval.py b/mythril/analysis/modules/unchecked_retval.py index 830d006e..5beb0fda 100644 --- a/mythril/analysis/modules/unchecked_retval.py +++ b/mythril/analysis/modules/unchecked_retval.py @@ -114,8 +114,11 @@ def _analyze_state(state: GlobalState) -> list: retval = state.mstate.stack[-1] # Use Typed Dict after release of mypy 0.670 and remove type ignore retvals.append( - {"address": state.instruction["address"] - 1, "retval": retval} - ) # type: ignore + { # type: ignore + "address": state.instruction["address"] - 1, + "retval": retval, + } + ) return [] diff --git a/mythril/laser/ethereum/state/machine_state.py b/mythril/laser/ethereum/state/machine_state.py index b71fe59d..0ee781ae 100644 --- a/mythril/laser/ethereum/state/machine_state.py +++ b/mythril/laser/ethereum/state/machine_state.py @@ -27,12 +27,11 @@ class MachineStack(list): """ super(MachineStack, self).__init__(default_list or []) - def append(self, element: Expression) -> None: + def append(self, element: Union[int, Expression]) -> None: """ :param element: element to be appended to the list :function: appends the element to list if the size is less than STACK_LIMIT, else throws an error """ - assert isinstance(element, Expression) if super(MachineStack, self).__len__() >= self.STACK_LIMIT: raise StackOverflowException( "Reached the EVM stack limit of {}, you can't append more " @@ -40,7 +39,7 @@ class MachineStack(list): ) super(MachineStack, self).append(element) - def pop(self, index=-1) -> Expression: + def pop(self, index=-1) -> Union[int, Expression]: """ :param index:index to be popped, same as the list() class. :returns popped value diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 5b594fef..6eb1b71e 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -81,6 +81,8 @@ def pop_bitvec(state: "MachineState") -> BitVec: symbol_factory.BitVecVal(1, 256), symbol_factory.BitVecVal(0, 256), ) + elif isinstance(item, int): + return symbol_factory.BitVecVal(item, 256) else: return simplify(item) diff --git a/mythril/support/signatures.py b/mythril/support/signatures.py index 3a113d99..e0deb9ea 100644 --- a/mythril/support/signatures.py +++ b/mythril/support/signatures.py @@ -7,7 +7,7 @@ import sqlite3 import time from collections import defaultdict from subprocess import PIPE, Popen -from typing import List, Set, DefaultDict +from typing import List, Set, DefaultDict, Dict from mythril.exceptions import CompilerError @@ -45,7 +45,7 @@ def synchronized(sync_lock): class Singleton(type): """A metaclass type implementing the singleton pattern.""" - _instances = dict() + _instances = dict() # type: Dict[Singleton, Singleton] @synchronized(lock) def __call__(cls, *args, **kwargs): From 8422a80ba669cf7a1f7f52bb17f50b03be8cb83a Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 16:57:23 +0530 Subject: [PATCH 27/42] Specify the reason for the ignoring jinja2.autoescape's type --- mythril/analysis/callgraph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mythril/analysis/callgraph.py b/mythril/analysis/callgraph.py index 0314a27e..ec68a1e7 100644 --- a/mythril/analysis/callgraph.py +++ b/mythril/analysis/callgraph.py @@ -3,6 +3,7 @@ graphs.""" import re +# ignore exists due to some problem in the typeshed https://github.com/python/mypy/issues/3589 from jinja2 import Environment, PackageLoader, select_autoescape # type: ignore from z3 import Z3Exception From 552d6737bd63ece15ea6af14f9702be3b8c92a58 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 17:13:01 +0530 Subject: [PATCH 28/42] Remove type ignore for jinja2 --- mythril/analysis/callgraph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/analysis/callgraph.py b/mythril/analysis/callgraph.py index ec68a1e7..7f85dd17 100644 --- a/mythril/analysis/callgraph.py +++ b/mythril/analysis/callgraph.py @@ -4,7 +4,7 @@ graphs.""" import re # ignore exists due to some problem in the typeshed https://github.com/python/mypy/issues/3589 -from jinja2 import Environment, PackageLoader, select_autoescape # type: ignore +from jinja2 import Environment, PackageLoader, select_autoescape from z3 import Z3Exception from mythril.laser.ethereum.svm import NodeFlags From adb89052828fd2ec296b2869adb885ffd2a128fa Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 17:13:21 +0530 Subject: [PATCH 29/42] Remove the type ignore's description comment --- mythril/analysis/callgraph.py | 1 - 1 file changed, 1 deletion(-) diff --git a/mythril/analysis/callgraph.py b/mythril/analysis/callgraph.py index 7f85dd17..b02a62fa 100644 --- a/mythril/analysis/callgraph.py +++ b/mythril/analysis/callgraph.py @@ -3,7 +3,6 @@ graphs.""" import re -# ignore exists due to some problem in the typeshed https://github.com/python/mypy/issues/3589 from jinja2 import Environment, PackageLoader, select_autoescape from z3 import Z3Exception From 1944936ed3a62119601029a61713e6ee2dcf7837 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 17:16:13 +0530 Subject: [PATCH 30/42] Remove type ignore int() --- mythril/laser/ethereum/state/account.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index cac97ecc..e5bb6fea 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -38,8 +38,7 @@ class Storage: int( self.dynld.read_storage( contract_address=self.address, - # The line below is expected to raise a ValueError - index=int(item), # type: ignore + index=int(item), ), 16, ), From 6ff814ed010cc8856341a5da2e2ef2f312bd1bf8 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 22 Jan 2019 17:18:52 +0530 Subject: [PATCH 31/42] Reformat account.py with black --- mythril/laser/ethereum/state/account.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mythril/laser/ethereum/state/account.py b/mythril/laser/ethereum/state/account.py index e5bb6fea..c13c3f2f 100644 --- a/mythril/laser/ethereum/state/account.py +++ b/mythril/laser/ethereum/state/account.py @@ -37,8 +37,7 @@ class Storage: self._storage[item] = symbol_factory.BitVecVal( int( self.dynld.read_storage( - contract_address=self.address, - index=int(item), + contract_address=self.address, index=int(item) ), 16, ), From 06b92199f0e7278bb4ced18387262f3826296ff8 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 23 Jan 2019 17:03:09 +0530 Subject: [PATCH 32/42] Fix the typehinting of svm.py and instructions.py for merge --- mythril/laser/ethereum/instructions.py | 8 +++----- mythril/laser/ethereum/svm.py | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index 48610551..d3bcd2d7 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -358,7 +358,7 @@ class Instruction: symbol_factory.BitVecVal(0, 248), Extract(offset + 7, offset, op1), ) - ) + ) # type: Union[int, Expression] else: result = 0 except TypeError: @@ -717,17 +717,15 @@ class Instruction: log.debug("Unsupported symbolic memory offset in CALLDATACOPY") return [global_state] - dstart_sym = False try: - dstart = util.get_concrete_int(op1) + dstart = util.get_concrete_int(op1) # type: Union[int, Expression] except TypeError: log.debug("Unsupported symbolic calldata offset in CALLDATACOPY") dstart = simplify(op1) - dstart_sym = True size_sym = False try: - size = util.get_concrete_int(op2) + size = util.get_concrete_int(op2) # type: Union[int, Expression] except TypeError: log.debug("Unsupported symbolic size in CALLDATACOPY") size = simplify(op2) diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index ecdb3634..a0f743e7 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -97,7 +97,7 @@ class LaserEVM: self.pre_hooks = defaultdict(list) # type: DefaultDict[str, List[Callable]] self.post_hooks = defaultdict(list) # type: DefaultDict[str, List[Callable]] - self._add_world_state_hooks = [] + self._add_world_state_hooks = [] # type: List[Callable] self.iprof = InstructionProfiler() if enable_iprof else None log.info("LASER EVM initialized with dynamic loader: " + str(dynamic_loader)) From 3f6dbacd9d02569f0fd071452efddbeb6b267c2f Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 23 Jan 2019 20:26:50 +0530 Subject: [PATCH 33/42] Fix all the mypy errors in instructions.py --- mythril/laser/ethereum/instructions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index d3bcd2d7..e48581a7 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -718,14 +718,14 @@ class Instruction: return [global_state] try: - dstart = util.get_concrete_int(op1) # type: Union[int, Expression] + dstart = util.get_concrete_int(op1) # type: Union[int, BitVec] except TypeError: log.debug("Unsupported symbolic calldata offset in CALLDATACOPY") dstart = simplify(op1) size_sym = False try: - size = util.get_concrete_int(op2) # type: Union[int, Expression] + size = util.get_concrete_int(op2) # type: Union[int, BitVec] except TypeError: log.debug("Unsupported symbolic size in CALLDATACOPY") size = simplify(op2) @@ -744,7 +744,7 @@ class Instruction: 8, ) return [global_state] - + size = cast(int, size) if size > 0: try: state.mem_extend(mstart, size) From a7dfe98f1ce1827d9a46cfdd5a6bc30b5859b3c5 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 23 Jan 2019 20:27:04 +0530 Subject: [PATCH 34/42] Fix more mypy errors --- mythril/laser/ethereum/state/calldata.py | 4 ++-- mythril/laser/ethereum/util.py | 2 +- mythril/laser/smt/bitvec.py | 1 + mythril/laser/smt/expression.py | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index e69714b7..4a170b2d 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -53,7 +53,7 @@ class BaseCalldata: parts = self[offset : offset + 32] return simplify(Concat(parts)) - def __getitem__(self, item: Union[int, slice]) -> Any: + def __getitem__(self, item: Union[int, slice, BitVec]) -> Any: """ :param item: @@ -273,7 +273,7 @@ class BasicSymbolicCalldata(BaseCalldata): def _load(self, item: Union[int, BitVec], clean=False) -> Any: expr_item = ( symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item - ) + ) # type: BitVec symbolic_base_value = If( expr_item >= self._size, diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 6eb1b71e..6f4f326a 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -75,7 +75,7 @@ def pop_bitvec(state: "MachineState") -> BitVec: item = state.stack.pop() - if type(item) == Bool: + if isinstance(item, Bool): return If( cast(Bool, item), symbol_factory.BitVecVal(1, 256), diff --git a/mythril/laser/smt/bitvec.py b/mythril/laser/smt/bitvec.py index feeac40b..a4905ff6 100644 --- a/mythril/laser/smt/bitvec.py +++ b/mythril/laser/smt/bitvec.py @@ -11,6 +11,7 @@ Annotations = List[Any] # fmt: off + class BitVec(Expression[z3.BitVecRef]): """A bit vector symbol.""" diff --git a/mythril/laser/smt/expression.py b/mythril/laser/smt/expression.py index 2ed166c7..120e37e8 100644 --- a/mythril/laser/smt/expression.py +++ b/mythril/laser/smt/expression.py @@ -46,7 +46,7 @@ class Expression(Generic[T]): return repr(self.raw) -def simplify(expression: Expression) -> Expression: +def simplify(expression: T) -> T: """Simplify the expression . :param expression: From dce26a37b274af1a2f35ba30e85f4b57d5120717 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 23 Jan 2019 20:52:57 +0530 Subject: [PATCH 35/42] Fix all the mypy errors --- mythril/laser/ethereum/instructions.py | 4 ++-- mythril/laser/ethereum/state/calldata.py | 16 +++++++++++----- mythril/laser/ethereum/state/memory.py | 1 + mythril/laser/ethereum/svm.py | 2 +- mythril/laser/ethereum/util.py | 1 + mythril/laser/smt/bitvec.py | 19 ++++++++++++++++++- 6 files changed, 34 insertions(+), 9 deletions(-) diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py index e48581a7..615c7d4a 100644 --- a/mythril/laser/ethereum/instructions.py +++ b/mythril/laser/ethereum/instructions.py @@ -718,14 +718,14 @@ class Instruction: return [global_state] try: - dstart = util.get_concrete_int(op1) # type: Union[int, BitVec] + dstart = util.get_concrete_int(op1) # type: Union[int, BitVec] except TypeError: log.debug("Unsupported symbolic calldata offset in CALLDATACOPY") dstart = simplify(op1) size_sym = False try: - size = util.get_concrete_int(op2) # type: Union[int, BitVec] + size = util.get_concrete_int(op2) # type: Union[int, BitVec] except TypeError: log.debug("Unsupported symbolic size in CALLDATACOPY") size = simplify(op2) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 4a170b2d..26780a4f 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -34,14 +34,14 @@ class BaseCalldata: self.tx_id = tx_id @property - def calldatasize(self) -> Expression: + def calldatasize(self) -> BitVec: """ :return: Calldata size for this calldata object """ result = self.size if isinstance(result, int): - return symbol_factory.BitVecVal(result, 256) + return BitVec(symbol_factory.BitVecVal(result, 256)) return result def get_word_at(self, offset: int) -> Expression: @@ -267,18 +267,24 @@ class BasicSymbolicCalldata(BaseCalldata): :param tx_id: Id of the transaction that the calldata is for. """ self._reads = [] # type: List[Tuple[Union[int, BitVec], BitVec]] - self._size = BitVec(str(tx_id) + "_calldatasize", 256) + self._size = BitVec(symbol_factory.BitVecSym(str(tx_id) + "_calldatasize", 256)) super().__init__(tx_id) def _load(self, item: Union[int, BitVec], clean=False) -> Any: expr_item = ( - symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item + BitVec(symbol_factory.BitVecVal(item, 256)) + if isinstance(item, int) + else item ) # type: BitVec symbolic_base_value = If( expr_item >= self._size, symbol_factory.BitVecVal(0, 8), - BitVec("{}_calldata_{}".format(self.tx_id, str(item)), 8), + BitVec( + symbol_factory.BitVecSym( + "{}_calldata_{}".format(self.tx_id, str(item)), 8 + ) + ), ) return_value = symbolic_base_value for r_index, r_value in self._reads: diff --git a/mythril/laser/ethereum/state/memory.py b/mythril/laser/ethereum/state/memory.py index affd118c..4fac4120 100644 --- a/mythril/laser/ethereum/state/memory.py +++ b/mythril/laser/ethereum/state/memory.py @@ -83,6 +83,7 @@ class Memory: assert len(_bytes) == 32 self[index : index + 32] = list(bytearray(_bytes)) except (Z3Exception, AttributeError): # BitVector or BoolRef + value = cast(Union[BitVec, Bool], value) if isinstance(value, Bool): value_to_write = If( value, diff --git a/mythril/laser/ethereum/svm.py b/mythril/laser/ethereum/svm.py index a0f743e7..a373db42 100644 --- a/mythril/laser/ethereum/svm.py +++ b/mythril/laser/ethereum/svm.py @@ -97,7 +97,7 @@ class LaserEVM: self.pre_hooks = defaultdict(list) # type: DefaultDict[str, List[Callable]] self.post_hooks = defaultdict(list) # type: DefaultDict[str, List[Callable]] - self._add_world_state_hooks = [] # type: List[Callable] + self._add_world_state_hooks = [] # type: List[Callable] self.iprof = InstructionProfiler() if enable_iprof else None log.info("LASER EVM initialized with dynamic loader: " + str(dynamic_loader)) diff --git a/mythril/laser/ethereum/util.py b/mythril/laser/ethereum/util.py index 6f4f326a..9cb5d950 100644 --- a/mythril/laser/ethereum/util.py +++ b/mythril/laser/ethereum/util.py @@ -84,6 +84,7 @@ def pop_bitvec(state: "MachineState") -> BitVec: elif isinstance(item, int): return symbol_factory.BitVecVal(item, 256) else: + item = cast(BitVec, item) return simplify(item) diff --git a/mythril/laser/smt/bitvec.py b/mythril/laser/smt/bitvec.py index a4905ff6..cfad231d 100644 --- a/mythril/laser/smt/bitvec.py +++ b/mythril/laser/smt/bitvec.py @@ -11,7 +11,6 @@ Annotations = List[Any] # fmt: off - class BitVec(Expression[z3.BitVecRef]): """A bit vector symbol.""" @@ -140,6 +139,24 @@ class BitVec(Expression[z3.BitVecRef]): union = self.annotations + other.annotations return Bool(self.raw > other.raw, annotations=union) + def __le__(self, other: "BitVec") -> Bool: + """Create a signed less than expression. + + :param other: + :return: + """ + union = self.annotations + other.annotations + return Bool(self.raw <= other.raw, annotations=union) + + def __ge__(self, other: "BitVec") -> Bool: + """Create a signed greater than expression. + + :param other: + :return: + """ + union = self.annotations + other.annotations + return Bool(self.raw >= other.raw, annotations=union) + # MYPY: fix complains about overriding __eq__ def __eq__(self, other: Union[int, "BitVec"]) -> Bool: # type: ignore """Create an equality expression. From 8b3522ef2321b6ccfac22573a32c91f0a95a1d82 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 23 Jan 2019 21:26:58 +0530 Subject: [PATCH 36/42] Fix the tests --- mythril/laser/ethereum/state/calldata.py | 7 ++----- mythril/laser/smt/bitvec.py | 1 + 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 26780a4f..7f016be2 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -41,7 +41,7 @@ class BaseCalldata: """ result = self.size if isinstance(result, int): - return BitVec(symbol_factory.BitVecVal(result, 256)) + return symbol_factory.BitVecVal(result, 256) return result def get_word_at(self, offset: int) -> Expression: @@ -138,10 +138,7 @@ class ConcreteCalldata(BaseCalldata): :param item: :return: """ - item = cast( - BitVec, - symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item, - ) + item = symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item return simplify(self._calldata[item]) def concrete(self, model: Model) -> list: diff --git a/mythril/laser/smt/bitvec.py b/mythril/laser/smt/bitvec.py index cfad231d..8347352c 100644 --- a/mythril/laser/smt/bitvec.py +++ b/mythril/laser/smt/bitvec.py @@ -11,6 +11,7 @@ Annotations = List[Any] # fmt: off + class BitVec(Expression[z3.BitVecRef]): """A bit vector symbol.""" From 689b6c469b8b689f416f8b0ef944f728f3e57bca Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 29 Jan 2019 21:26:34 +0530 Subject: [PATCH 37/42] Fix some mypy errors --- mythril/laser/smt/bool.py | 10 +++++----- mythril/support/support_utils.py | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mythril/laser/smt/bool.py b/mythril/laser/smt/bool.py index c82e4f1f..aba401c3 100644 --- a/mythril/laser/smt/bool.py +++ b/mythril/laser/smt/bool.py @@ -1,7 +1,7 @@ """This module provides classes for an SMT abstraction of boolean expressions.""" -from typing import Union, cast +from typing import Union, cast, List import z3 @@ -81,13 +81,13 @@ class Bool(Expression[z3.BoolRef]): return False -def And(*args: Bool) -> Bool: +def And(*args: Union[Bool, bool]) -> Bool: """Create an And expression.""" union = [] - args = [arg if isinstance(arg, Bool) else Bool(arg) for arg in args] - for arg in args: + args_list = [arg if isinstance(arg, Bool) else Bool(arg) for arg in args] # type: List[Bool] + for arg in args_list: union.append(arg.annotations) - return Bool(z3.And([a.raw for a in args]), union) + return Bool(z3.And([a.raw for a in args_list]), union) def Or(a: Bool, b: Bool) -> Bool: diff --git a/mythril/support/support_utils.py b/mythril/support/support_utils.py index 9fe90a8f..dd324799 100644 --- a/mythril/support/support_utils.py +++ b/mythril/support/support_utils.py @@ -1,10 +1,10 @@ """This module contains utility functions for the Mythril support package.""" - +from typing import Dict class Singleton(type): """A metaclass type implementing the singleton pattern.""" - _instances = {} + _instances = {} # type: Dict def __call__(cls, *args, **kwargs): """Delegate the call to an existing resource or a a new one. From 6edbc72dce002526a2187e0f14b5303433e79279 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Tue, 29 Jan 2019 21:52:15 +0530 Subject: [PATCH 38/42] Fix black errors --- mythril/support/support_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mythril/support/support_utils.py b/mythril/support/support_utils.py index dd324799..b437d795 100644 --- a/mythril/support/support_utils.py +++ b/mythril/support/support_utils.py @@ -1,6 +1,7 @@ """This module contains utility functions for the Mythril support package.""" from typing import Dict + class Singleton(type): """A metaclass type implementing the singleton pattern.""" From 006b3d1c95f32e22e1c2ba0fbff6793e15a72c45 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 30 Jan 2019 11:50:27 +0530 Subject: [PATCH 39/42] Remove BitVec() --- mythril/laser/ethereum/state/calldata.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 7f016be2..224a86eb 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -264,7 +264,7 @@ class BasicSymbolicCalldata(BaseCalldata): :param tx_id: Id of the transaction that the calldata is for. """ self._reads = [] # type: List[Tuple[Union[int, BitVec], BitVec]] - self._size = BitVec(symbol_factory.BitVecSym(str(tx_id) + "_calldatasize", 256)) + self._size = symbol_factory.BitVecSym(str(tx_id) + "_calldatasize", 256) super().__init__(tx_id) def _load(self, item: Union[int, BitVec], clean=False) -> Any: From 5a4bdc97fd410d4d66cc7338e967ea4f66a6ea00 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Wed, 30 Jan 2019 11:51:14 +0530 Subject: [PATCH 40/42] Remove BitVec() in expr --- mythril/laser/ethereum/state/calldata.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mythril/laser/ethereum/state/calldata.py b/mythril/laser/ethereum/state/calldata.py index 224a86eb..a8ebfa2b 100644 --- a/mythril/laser/ethereum/state/calldata.py +++ b/mythril/laser/ethereum/state/calldata.py @@ -269,9 +269,7 @@ class BasicSymbolicCalldata(BaseCalldata): def _load(self, item: Union[int, BitVec], clean=False) -> Any: expr_item = ( - BitVec(symbol_factory.BitVecVal(item, 256)) - if isinstance(item, int) - else item + symbol_factory.BitVecVal(item, 256) if isinstance(item, int) else item ) # type: BitVec symbolic_base_value = If( From 36fd075836ed5d51751fe1b4b430f3828cf5c3e9 Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 1 Feb 2019 17:54:48 +0530 Subject: [PATCH 41/42] Remove extra type definitions, add todo, use Baseclass over Union[SymbolicCalldata, ConcreteCalldata] --- mythril/laser/ethereum/strategy/basic.py | 2 +- mythril/laser/ethereum/transaction/transaction_models.py | 4 ++-- mythril/laser/smt/bool.py | 2 +- mythril/laser/smt/expression.py | 5 ++++- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/mythril/laser/ethereum/strategy/basic.py b/mythril/laser/ethereum/strategy/basic.py index 9e9eaf12..5930f4e6 100644 --- a/mythril/laser/ethereum/strategy/basic.py +++ b/mythril/laser/ethereum/strategy/basic.py @@ -14,7 +14,7 @@ except ImportError: from random import random from bisect import bisect - # Remove ignore after this has been fixed: https://github.com/python/mypy/issues/1297 + # TODO: Remove ignore after this has been fixed: https://github.com/python/mypy/issues/1297 def choices( # type: ignore population: List, weights: List[int] = None ) -> List[int]: diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 6deefac3..05fc701f 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -87,10 +87,10 @@ class BaseTransaction: if call_data is None and init_call_data: self.call_data = SymbolicCalldata( self.id - ) # type: Union[SymbolicCalldata, ConcreteCalldata] + ) # type: BaseCalldata else: self.call_data = ( - cast(Union[SymbolicCalldata, ConcreteCalldata], call_data) + call_data if isinstance(call_data, BaseCalldata) else ConcreteCalldata(self.id, []) ) diff --git a/mythril/laser/smt/bool.py b/mythril/laser/smt/bool.py index aba401c3..9fa097e4 100644 --- a/mythril/laser/smt/bool.py +++ b/mythril/laser/smt/bool.py @@ -84,7 +84,7 @@ class Bool(Expression[z3.BoolRef]): def And(*args: Union[Bool, bool]) -> Bool: """Create an And expression.""" union = [] - args_list = [arg if isinstance(arg, Bool) else Bool(arg) for arg in args] # type: List[Bool] + args_list = [arg if isinstance(arg, Bool) else Bool(arg) for arg in args] for arg in args_list: union.append(arg.annotations) return Bool(z3.And([a.raw for a in args_list]), union) diff --git a/mythril/laser/smt/expression.py b/mythril/laser/smt/expression.py index 120e37e8..8e9e697e 100644 --- a/mythril/laser/smt/expression.py +++ b/mythril/laser/smt/expression.py @@ -46,7 +46,10 @@ class Expression(Generic[T]): return repr(self.raw) -def simplify(expression: T) -> T: +G = TypeVar("G", bound=Expression) + + +def simplify(expression: G) -> G: """Simplify the expression . :param expression: From 91bae575ca9b7c544e7c002b734d9dec5830c3ca Mon Sep 17 00:00:00 2001 From: Nikhil Parasaram Date: Fri, 1 Feb 2019 17:57:49 +0530 Subject: [PATCH 42/42] Reformat code --- mythril/laser/ethereum/transaction/transaction_models.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py index 05fc701f..def9a494 100644 --- a/mythril/laser/ethereum/transaction/transaction_models.py +++ b/mythril/laser/ethereum/transaction/transaction_models.py @@ -85,9 +85,7 @@ class BaseTransaction: self.caller = caller self.callee_account = callee_account if call_data is None and init_call_data: - self.call_data = SymbolicCalldata( - self.id - ) # type: BaseCalldata + self.call_data = SymbolicCalldata(self.id) # type: BaseCalldata else: self.call_data = ( call_data