Apply better doc string formatting

pull/845/head
Dominik Muhs 6 years ago
parent a8f61a0f83
commit 5e0bcd3f9f
  1. 3
      mythril/analysis/call_helpers.py
  2. 3
      mythril/analysis/callgraph.py
  3. 17
      mythril/analysis/modules/base.py
  4. 4
      mythril/analysis/modules/delegatecall.py
  5. 25
      mythril/analysis/modules/dependence_on_predictable_vars.py
  6. 4
      mythril/analysis/modules/deprecated_ops.py
  7. 10
      mythril/analysis/modules/ether_thief.py
  8. 8
      mythril/analysis/modules/exceptions.py
  9. 10
      mythril/analysis/modules/external_calls.py
  10. 23
      mythril/analysis/modules/integer.py
  11. 7
      mythril/analysis/modules/multiple_sends.py
  12. 3
      mythril/analysis/modules/suicide.py
  13. 25
      mythril/analysis/modules/transaction_order_dependence.py
  14. 3
      mythril/analysis/modules/unchecked_retval.py
  15. 3
      mythril/analysis/ops.py
  16. 2
      mythril/analysis/report.py
  17. 3
      mythril/analysis/security.py
  18. 6
      mythril/analysis/symbolic.py
  19. 3
      mythril/analysis/traceexplore.py
  20. 8
      mythril/disassembler/asm.py
  21. 12
      mythril/disassembler/disassembly.py
  22. 6
      mythril/ethereum/evmcontract.py
  23. 26
      mythril/ethereum/interface/leveldb/accountindexing.py
  24. 20
      mythril/ethereum/interface/leveldb/client.py
  25. 15
      mythril/ethereum/interface/leveldb/eth_db.py
  26. 30
      mythril/ethereum/interface/leveldb/state.py
  27. 3
      mythril/ethereum/interface/rpc/constants.py
  28. 12
      mythril/ethereum/interface/rpc/exceptions.py
  29. 15
      mythril/ethereum/interface/rpc/utils.py
  30. 3
      mythril/ethereum/util.py
  31. 12
      mythril/exceptions.py
  32. 10
      mythril/interfaces/epic.py
  33. 25
      mythril/laser/ethereum/call.py
  34. 2
      mythril/laser/ethereum/cfg.py
  35. 3
      mythril/laser/ethereum/gas.py
  36. 17
      mythril/laser/ethereum/instructions.py
  37. 7
      mythril/laser/ethereum/keccak.py
  38. 2
      mythril/laser/ethereum/natives.py
  39. 16
      mythril/laser/ethereum/state/account.py
  40. 24
      mythril/laser/ethereum/state/annotation.py
  41. 19
      mythril/laser/ethereum/state/calldata.py
  42. 10
      mythril/laser/ethereum/state/constraints.py
  43. 6
      mythril/laser/ethereum/state/environment.py
  44. 14
      mythril/laser/ethereum/state/global_state.py
  45. 26
      mythril/laser/ethereum/state/machine_state.py
  46. 6
      mythril/laser/ethereum/state/memory.py
  47. 28
      mythril/laser/ethereum/state/world_state.py
  48. 8
      mythril/laser/ethereum/strategy/__init__.py
  49. 29
      mythril/laser/ethereum/strategy/basic.py
  50. 18
      mythril/laser/ethereum/svm.py
  51. 41
      mythril/laser/ethereum/taint_analysis.py
  52. 7
      mythril/laser/ethereum/transaction/concolic.py
  53. 9
      mythril/laser/ethereum/transaction/symbolic.py
  54. 15
      mythril/laser/ethereum/transaction/transaction_models.py
  55. 3
      mythril/laser/ethereum/util.py
  56. 33
      mythril/laser/smt/__init__.py
  57. 20
      mythril/laser/smt/array.py
  58. 3
      mythril/laser/smt/bitvec.py
  59. 3
      mythril/laser/smt/bool.py
  60. 21
      mythril/laser/smt/expression.py
  61. 26
      mythril/laser/smt/solver.py
  62. 5
      mythril/mythril.py
  63. 3
      mythril/solidity/soliditycontract.py
  64. 3
      mythril/support/loader.py
  65. 31
      mythril/support/signatures.py
  66. 3
      mythril/support/truffle.py
  67. 3
      mythril/version.py
  68. 14
      setup.py
  69. 8
      tests/__init__.py
  70. 16
      tests/evmcontract_test.py
  71. 4
      tests/native_test.py
  72. 12
      tests/report_test.py
  73. 8
      tests/rpc_test.py

@ -1,4 +1,5 @@
"""This module provides helper functions for the analysis modules to deal with call functionality.""" """This module provides helper functions for the analysis modules to deal with
call functionality."""
from typing import Union from typing import Union
from mythril.analysis.ops import VarType, Call, get_variable from mythril.analysis.ops import VarType, Call, get_variable

@ -1,4 +1,5 @@
"""This module contains the configuration and functions to create call graphs.""" """This module contains the configuration and functions to create call
graphs."""
import re import re

@ -1,4 +1,5 @@
"""This module contains the base class for all user-defined detection modules.""" """This module contains the base class for all user-defined detection
modules."""
import logging import logging
from typing import List from typing import List
@ -8,7 +9,7 @@ log = logging.getLogger(__name__)
class DetectionModule: class DetectionModule:
"""The base detection module. """The base detection module.
All custom-built detection modules must inherit from this class. All custom-built detection modules must inherit from this class.
""" """
@ -22,11 +23,11 @@ class DetectionModule:
) -> None: ) -> None:
"""Initialize a new detection module. """Initialize a new detection module.
:param name: :param name:
:param swc_id: :param swc_id:
:param hooks: :param hooks:
:param description: :param description:
:param entrypoint: :param entrypoint:
""" """
self.name = name self.name = name
self.swc_id = swc_id self.swc_id = swc_id
@ -40,7 +41,7 @@ class DetectionModule:
self.entrypoint = entrypoint self.entrypoint = entrypoint
def execute(self, statespace): def execute(self, statespace):
""" The entry point for execution, which is being called by Mythril. """The entry point for execution, which is being called by Mythril.
:param statespace: :param statespace:
:return: :return:

@ -18,9 +18,7 @@ class DelegateCallModule(DetectionModule):
"""This module detects calldata being forwarded using DELEGATECALL.""" """This module detects calldata being forwarded using DELEGATECALL."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="DELEGATECALL Usage in Fallback Function", name="DELEGATECALL Usage in Fallback Function",
swc_id=DELEGATECALL_TO_UNTRUSTED_CONTRACT, swc_id=DELEGATECALL_TO_UNTRUSTED_CONTRACT,

@ -1,4 +1,5 @@
"""This module contains the detection code for predictable variable dependence.""" """This module contains the detection code for predictable variable
dependence."""
import re import re
from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.state.global_state import GlobalState
from mythril.analysis.ops import Call, VarType from mythril.analysis.ops import Call, VarType
@ -14,12 +15,11 @@ log = logging.getLogger(__name__)
class PredictableDependenceModule(DetectionModule): class PredictableDependenceModule(DetectionModule):
"""This module detects whether Ether is sent using predictable parameters.""" """This module detects whether Ether is sent using predictable
parameters."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="Dependence of Predictable Variables", name="Dependence of Predictable Variables",
swc_id="{} {}".format(TIMESTAMP_DEPENDENCE, PREDICTABLE_VARS_DEPENDENCE), swc_id="{} {}".format(TIMESTAMP_DEPENDENCE, PREDICTABLE_VARS_DEPENDENCE),
@ -169,12 +169,15 @@ def _analyze_states(state: GlobalState) -> list:
r = re.search(r"storage_([a-z0-9_&^]+)", str(constraint)) r = re.search(r"storage_([a-z0-9_&^]+)", str(constraint))
if r: # block.blockhash(storage_0) if r: # block.blockhash(storage_0)
""" """We actually can do better here by adding a constraint
We actually can do better here by adding a constraint blockhash_block_storage_0 == 0 blockhash_block_storage_0 == 0 and checking model
and checking model satisfiability. When this is done, severity can be raised satisfiability.
from 'Informational' to 'Warning'.
Checking that storage at given index can be tainted is not necessary, since it usually contains When this is done, severity can be raised from
block.number of the 'commit' transaction in commit-reveal workflow. 'Informational' to 'Warning'. Checking that storage
at given index can be tainted is not necessary,
since it usually contains block.number of the
'commit' transaction in commit-reveal workflow.
""" """
index = r.group(1) index = r.group(1)

@ -60,9 +60,7 @@ class DeprecatedOperationsModule(DetectionModule):
"""This module checks for the usage of deprecated op codes.""" """This module checks for the usage of deprecated op codes."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="Deprecated Operations", name="Deprecated Operations",
swc_id=DEPRICATED_FUNCTIONS_USAGE, swc_id=DEPRICATED_FUNCTIONS_USAGE,

@ -1,4 +1,5 @@
"""This module contains the detection code for unauthorized ether withdrawal.""" """This module contains the detection code for unauthorized ether
withdrawal."""
from mythril.analysis.ops import * from mythril.analysis.ops import *
from mythril.analysis import solver from mythril.analysis import solver
from mythril.analysis.report import Issue from mythril.analysis.report import Issue
@ -83,12 +84,11 @@ def _analyze_state(state):
class EtherThief(DetectionModule): class EtherThief(DetectionModule):
"""This module search for cases where Ether can be withdrawn to a user-specified address.""" """This module search for cases where Ether can be withdrawn to a user-
specified address."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="Ether Thief", name="Ether Thief",
swc_id=UNPROTECTED_ETHER_WITHDRAWAL, swc_id=UNPROTECTED_ETHER_WITHDRAWAL,

@ -56,14 +56,10 @@ def _analyze_state(state) -> list:
class ReachableExceptionsModule(DetectionModule): class ReachableExceptionsModule(DetectionModule):
""" """"""
"""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="Reachable Exceptions", name="Reachable Exceptions",
swc_id=ASSERT_VIOLATION, swc_id=ASSERT_VIOLATION,

@ -1,4 +1,5 @@
"""This module contains the detection code for potentially insecure low-level calls.""" """This module contains the detection code for potentially insecure low-level
calls."""
from mythril.analysis.report import Issue from mythril.analysis.report import Issue
from mythril.analysis import solver from mythril.analysis import solver
from mythril.analysis.swc_data import REENTRANCY from mythril.analysis.swc_data import REENTRANCY
@ -96,12 +97,11 @@ def _analyze_state(state):
class ExternalCalls(DetectionModule): class ExternalCalls(DetectionModule):
"""This module searches for low level calls (e.g. call.value()) that forward all gas to the callee.""" """This module searches for low level calls (e.g. call.value()) that
forward all gas to the callee."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="External calls", name="External calls",
swc_id=REENTRANCY, swc_id=REENTRANCY,

@ -1,4 +1,5 @@
"""This module contains the detection code for integer overflows and underflows.""" """This module contains the detection code for integer overflows and
underflows."""
from mythril.analysis import solver from mythril.analysis import solver
from mythril.analysis.report import Issue from mythril.analysis.report import Issue
from mythril.analysis.swc_data import INTEGER_OVERFLOW_AND_UNDERFLOW from mythril.analysis.swc_data import INTEGER_OVERFLOW_AND_UNDERFLOW
@ -26,9 +27,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
"""This module searches for integer over- and underflows.""" """This module searches for integer over- and underflows."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="Integer Overflow and Underflow", name="Integer Overflow and Underflow",
swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW, swc_id=INTEGER_OVERFLOW_AND_UNDERFLOW,
@ -41,7 +40,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
) )
def execute(self, statespace): def execute(self, statespace):
"""Executes analysis module for integer underflow and integer overflow """Executes analysis module for integer underflow and integer overflow.
:param statespace: Statespace to analyse :param statespace: Statespace to analyse
:return: Found issues :return: Found issues
@ -137,7 +136,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
def _verify_integer_overflow( def _verify_integer_overflow(
self, statespace, node, expr, state, model, constraint, op0, op1 self, statespace, node, expr, state, model, constraint, op0, op1
): ):
""" Verifies existence of integer overflow """Verifies existence of integer overflow.
:param statespace: :param statespace:
:param node: :param node:
@ -178,8 +177,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
return None return None
def _check_integer_underflow(self, statespace, state, node): def _check_integer_underflow(self, statespace, state, node):
""" """Checks for integer underflow.
Checks for integer underflow
:param statespace: :param statespace:
:param state: state from node to examine :param state: state from node to examine
@ -267,7 +265,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
@staticmethod @staticmethod
def _check_jumpi(state, taint_result): def _check_jumpi(state, taint_result):
""" Check if conditional jump is dependent on the result of expression """Check if conditional jump is dependent on the result of expression.
:param state: :param state:
:param taint_result: :param taint_result:
@ -278,7 +276,7 @@ class IntegerOverflowUnderflowModule(DetectionModule):
@staticmethod @staticmethod
def _check_sstore(state, taint_result): def _check_sstore(state, taint_result):
""" Check if store operation is dependent on the result of expression """Check if store operation is dependent on the result of expression.
:param state: :param state:
:param taint_result: :param taint_result:
@ -298,9 +296,8 @@ class IntegerOverflowUnderflowModule(DetectionModule):
depth=0, depth=0,
max_depth=64, max_depth=64,
): ):
""" """Checks the statespace for children states, with JUMPI or SSTORE
Checks the statespace for children states, with JUMPI or SSTORE instuctions, instuctions, for dependency on expression.
for dependency on expression
:param statespace: The statespace to explore :param statespace: The statespace to explore
:param node: Current node to explore from :param node: Current node to explore from

@ -1,4 +1,5 @@
"""This module contains the detection code to find multiple sends occurring in a single transaction.""" """This module contains the detection code to find multiple sends occurring in
a single transaction."""
from mythril.analysis.report import Issue from mythril.analysis.report import Issue
from mythril.analysis.swc_data import MULTIPLE_SENDS from mythril.analysis.swc_data import MULTIPLE_SENDS
from mythril.analysis.modules.base import DetectionModule from mythril.analysis.modules.base import DetectionModule
@ -9,9 +10,7 @@ class MultipleSendsModule(DetectionModule):
"""This module checks for multiple sends in a single transaction.""" """This module checks for multiple sends in a single transaction."""
def __init__(self): def __init__(self):
""" """"""
"""
super().__init__( super().__init__(
name="Multiple Sends", name="Multiple Sends",
swc_id=MULTIPLE_SENDS, swc_id=MULTIPLE_SENDS,

@ -59,7 +59,8 @@ def _analyze_state(state):
class SuicideModule(DetectionModule): class SuicideModule(DetectionModule):
"""This module checks if the contact can be 'accidentally' killed by anyone.""" """This module checks if the contact can be 'accidentally' killed by
anyone."""
def __init__(self): def __init__(self):
super().__init__( super().__init__(

@ -1,4 +1,5 @@
"""This module contains the detection code to find the existence of transaction order dependence.""" """This module contains the detection code to find the existence of transaction
order dependence."""
import logging import logging
import re import re
import copy import copy
@ -31,7 +32,7 @@ class TxOrderDependenceModule(DetectionModule):
) )
def execute(self, statespace): def execute(self, statespace):
""" Executes the analysis module """Executes the analysis module.
:param statespace: :param statespace:
:return: :return:
@ -76,7 +77,7 @@ class TxOrderDependenceModule(DetectionModule):
# TODO: move to __init__ or util module # TODO: move to __init__ or util module
@staticmethod @staticmethod
def _get_states_with_opcode(statespace, opcode): def _get_states_with_opcode(statespace, opcode):
""" Gets all (state, node) tuples in statespace with opcode """Gets all (state, node) tuples in statespace with opcode.
:param statespace: :param statespace:
:param opcode: :param opcode:
@ -89,7 +90,8 @@ class TxOrderDependenceModule(DetectionModule):
@staticmethod @staticmethod
def _dependent_on_storage(expression): def _dependent_on_storage(expression):
""" Checks if expression is dependent on a storage symbol and returns the influencing storages """Checks if expression is dependent on a storage symbol and returns
the influencing storages.
:param expression: :param expression:
:return: :return:
@ -99,8 +101,8 @@ class TxOrderDependenceModule(DetectionModule):
@staticmethod @staticmethod
def _get_storage_variable(storage, state): def _get_storage_variable(storage, state):
""" """Get storage z3 object given storage name and the state.
Get storage z3 object given storage name and the state
:param storage: storage name example: storage_0 :param storage: storage name example: storage_0
:param state: state to retrieve the variable from :param state: state to retrieve the variable from
:return: z3 object representing storage :return: z3 object representing storage
@ -112,7 +114,7 @@ class TxOrderDependenceModule(DetectionModule):
return None return None
def _can_change(self, constraints, variable): def _can_change(self, constraints, variable):
""" Checks if the variable can change given some constraints """Checks if the variable can change given some constraints.
:param constraints: :param constraints:
:param variable: :param variable:
@ -133,7 +135,8 @@ class TxOrderDependenceModule(DetectionModule):
return False return False
def _get_influencing_storages(self, call): def _get_influencing_storages(self, call):
""" Examines a Call object and returns an iterator of all storages that influence the call value or direction """Examines a Call object and returns an iterator of all storages that
influence the call value or direction.
:param call: :param call:
""" """
@ -156,7 +159,7 @@ class TxOrderDependenceModule(DetectionModule):
yield storage yield storage
def _get_influencing_sstores(self, statespace, interesting_storages): def _get_influencing_sstores(self, statespace, interesting_storages):
""" Gets sstore (state, node) tuples that write to interesting_storages """Gets sstore (state, node) tuples that write to interesting_storages.
:param statespace: :param statespace:
:param interesting_storages: :param interesting_storages:
@ -175,8 +178,8 @@ class TxOrderDependenceModule(DetectionModule):
# TODO: remove # TODO: remove
@staticmethod @staticmethod
def _try_constraints(constraints, new_constraints): def _try_constraints(constraints, new_constraints):
""" """Tries new constraints.
Tries new constraints
:param constraints: :param constraints:
:param new_constraints: :param new_constraints:
:return Model if satisfiable otherwise None :return Model if satisfiable otherwise None

@ -1,4 +1,5 @@
"""This module contains detection code to find occurrences of calls whose return value remains unchecked.""" """This module contains detection code to find occurrences of calls whose
return value remains unchecked."""
from mythril.analysis.report import Issue from mythril.analysis.report import Issue
from mythril.analysis.swc_data import UNCHECKED_RET_VAL from mythril.analysis.swc_data import UNCHECKED_RET_VAL
from mythril.analysis.modules.base import DetectionModule from mythril.analysis.modules.base import DetectionModule

@ -1,4 +1,5 @@
"""This module contains various helper methods for dealing with EVM operations.""" """This module contains various helper methods for dealing with EVM
operations."""
from mythril.laser.smt import simplify from mythril.laser.smt import simplify
from enum import Enum from enum import Enum
from mythril.laser.ethereum import util from mythril.laser.ethereum import util

@ -156,7 +156,7 @@ class Report:
return json.dumps(result, sort_keys=True) return json.dumps(result, sort_keys=True)
def as_swc_standard_format(self): def as_swc_standard_format(self):
""" Format defined for integration and correlation """Format defined for integration and correlation.
:return: :return:
""" """

@ -1,4 +1,5 @@
"""This module contains functionality for hooking in detection modules and executing them.""" """This module contains functionality for hooking in detection modules and
executing them."""
from collections import defaultdict from collections import defaultdict
from ethereum.opcodes import opcodes from ethereum.opcodes import opcodes
from mythril.analysis import modules from mythril.analysis import modules

@ -1,4 +1,5 @@
"""This module contains a wrapper around LASER for extended analysis purposes.""" """This module contains a wrapper around LASER for extended analysis
purposes."""
from mythril.analysis.security import get_detection_module_hooks from mythril.analysis.security import get_detection_module_hooks
from mythril.laser.ethereum import svm from mythril.laser.ethereum import svm
from mythril.laser.ethereum.state.account import Account from mythril.laser.ethereum.state.account import Account
@ -16,7 +17,8 @@ from mythril.laser.ethereum.strategy.basic import (
class SymExecWrapper: class SymExecWrapper:
"""Wrapper class for the LASER Symbolic virtual machine. """Wrapper class for the LASER Symbolic virtual machine.
Symbolically executes the code and does a bit of pre-analysis for convenience. Symbolically executes the code and does a bit of pre-analysis for
convenience.
""" """
def __init__( def __init__(

@ -1,4 +1,5 @@
"""This module provides a function to convert a state space into a set of state nodes and transition edges.""" """This module provides a function to convert a state space into a set of state
nodes and transition edges."""
from z3 import Z3Exception from z3 import Z3Exception
from mythril.laser.smt import simplify from mythril.laser.smt import simplify
from mythril.laser.ethereum.svm import NodeFlags from mythril.laser.ethereum.svm import NodeFlags

@ -1,4 +1,5 @@
"""This module contains various helper classes and functions to deal with EVM code disassembly.""" """This module contains various helper classes and functions to deal with EVM
code disassembly."""
import re import re
from collections import Generator from collections import Generator
@ -12,7 +13,7 @@ opcodes[254] = ["ASSERT_FAIL", 0, 0, 0]
class EvmInstruction: class EvmInstruction:
""" Model to hold the information of the disassembly.""" """Model to hold the information of the disassembly."""
def __init__(self, address, op_code, argument=None): def __init__(self, address, op_code, argument=None):
self.address = address self.address = address
@ -60,7 +61,8 @@ def get_opcode_from_name(operation_name: str) -> int:
def find_op_code_sequence(pattern: list, instruction_list: list) -> Generator: def find_op_code_sequence(pattern: list, instruction_list: list) -> Generator:
"""Returns all indices in instruction_list that point to instruction sequences following a pattern. """Returns all indices in instruction_list that point to instruction
sequences following a pattern.
:param pattern: The pattern to look for, e.g. [["PUSH1", "PUSH2"], ["EQ"]] where ["PUSH1", "EQ"] satisfies pattern :param pattern: The pattern to look for, e.g. [["PUSH1", "PUSH2"], ["EQ"]] where ["PUSH1", "EQ"] satisfies pattern
:param instruction_list: List of instructions to look in :param instruction_list: List of instructions to look in

@ -5,8 +5,7 @@ from mythril.support.signatures import SignatureDB
class Disassembly(object): class Disassembly(object):
""" """Disassembly class.
Disassembly class
Stores bytecode, and its disassembly. Stores bytecode, and its disassembly.
Additionally it will gather the following information on the existing functions in the disassembled code: Additionally it will gather the following information on the existing functions in the disassembled code:
@ -58,10 +57,11 @@ class Disassembly(object):
def get_function_info( def get_function_info(
index: int, instruction_list: list, signature_database: SignatureDB index: int, instruction_list: list, signature_database: SignatureDB
) -> (str, int, str): ) -> (str, int, str):
""" """Finds the function information for a call table entry Solidity uses the
Finds the function information for a call table entry first 4 bytes of the calldata to indicate which function the message call
Solidity uses the first 4 bytes of the calldata to indicate which function the message call should execute should execute The generated code that directs execution to the correct
The generated code that directs execution to the correct function looks like this: function looks like this:
- PUSH function_hash - PUSH function_hash
- EQ - EQ
- PUSH entry_point - PUSH entry_point

@ -1,4 +1,5 @@
"""This module contains the class representing EVM contracts, aka Smart Contracts.""" """This module contains the class representing EVM contracts, aka Smart
Contracts."""
from mythril.disassembler.disassembly import Disassembly from mythril.disassembler.disassembly import Disassembly
from ethereum import utils from ethereum import utils
import persistent import persistent
@ -6,7 +7,8 @@ import re
class EVMContract(persistent.Persistent): class EVMContract(persistent.Persistent):
"""This class represents an address with associated code (Smart Contract).""" """This class represents an address with associated code (Smart
Contract)."""
def __init__( def __init__(
self, code="", creation_code="", name="Unknown", enable_online_lookup=False self, code="", creation_code="", name="Unknown", enable_online_lookup=False

@ -1,7 +1,7 @@
"""This module contains account indexing functionality. """This module contains account indexing functionality.
This includes a sedes class for lists, account storage receipts for LevelDB This includes a sedes class for lists, account storage receipts for
and a class for updating account addresses. LevelDB and a class for updating account addresses.
""" """
import logging import logging
from mythril import ethereum from mythril import ethereum
@ -20,6 +20,7 @@ BATCH_SIZE = 8 * 4096
class CountableList(object): class CountableList(object):
"""A sedes for lists of arbitrary length. """A sedes for lists of arbitrary length.
:param element_sedes: when (de-)serializing a list, this sedes will be applied to all of its elements :param element_sedes: when (de-)serializing a list, this sedes will be applied to all of its elements
""" """
@ -50,9 +51,7 @@ class CountableList(object):
class ReceiptForStorage(rlp.Serializable): class ReceiptForStorage(rlp.Serializable):
""" """Receipt format stored in levelDB."""
Receipt format stored in levelDB
"""
fields = [ fields = [
("state_root", binary), ("state_root", binary),
@ -66,9 +65,7 @@ class ReceiptForStorage(rlp.Serializable):
class AccountIndexer(object): class AccountIndexer(object):
""" """Updates address index."""
Updates address index
"""
def __init__(self, ethDB): def __init__(self, ethDB):
self.db = ethDB self.db = ethDB
@ -78,9 +75,8 @@ class AccountIndexer(object):
self.updateIfNeeded() self.updateIfNeeded()
def get_contract_by_hash(self, contract_hash): def get_contract_by_hash(self, contract_hash):
""" """get mapped contract_address by its hash, if not found try
get mapped contract_address by its hash, if not found try indexing indexing."""
"""
contract_address = self.db.reader._get_address_by_hash(contract_hash) contract_address = self.db.reader._get_address_by_hash(contract_hash)
if contract_address is not None: if contract_address is not None:
return contract_address return contract_address
@ -89,9 +85,7 @@ class AccountIndexer(object):
raise AddressNotFoundError raise AddressNotFoundError
def _process(self, startblock): def _process(self, startblock):
""" """Processesing method."""
Processesing method
"""
log.debug("Processing blocks %d to %d" % (startblock, startblock + BATCH_SIZE)) log.debug("Processing blocks %d to %d" % (startblock, startblock + BATCH_SIZE))
addresses = [] addresses = []
@ -113,9 +107,7 @@ class AccountIndexer(object):
return addresses return addresses
def updateIfNeeded(self): def updateIfNeeded(self):
""" """update address index."""
update address index
"""
headBlock = self.db.reader._get_head_block() headBlock = self.db.reader._get_head_block()
if headBlock is not None: if headBlock is not None:
# avoid restarting search if head block is same & we already initialized # avoid restarting search if head block is same & we already initialized

@ -158,9 +158,7 @@ class LevelDBReader(object):
class LevelDBWriter(object): class LevelDBWriter(object):
""" """level db writing interface."""
level db writing interface
"""
def __init__(self, db): def __init__(self, db):
""" """
@ -183,7 +181,7 @@ class LevelDBWriter(object):
self.wb = self.db.write_batch() self.wb = self.db.write_batch()
def _commit_batch(self): def _commit_batch(self):
"""Commit a batch""" """Commit a batch."""
self.wb.write() self.wb.write()
def _store_account_address(self, address): def _store_account_address(self, address):
@ -196,9 +194,7 @@ class LevelDBWriter(object):
class EthLevelDB(object): class EthLevelDB(object):
""" """Go-Ethereum LevelDB client class."""
Go-Ethereum LevelDB client class
"""
def __init__(self, path): def __init__(self, path):
""" """
@ -235,10 +231,12 @@ class EthLevelDB(object):
try: try:
address = _encode_hex(indexer.get_contract_by_hash(address_hash)) address = _encode_hex(indexer.get_contract_by_hash(address_hash))
except AddressNotFoundError: except AddressNotFoundError:
""" """The hash->address mapping does not exist in our index.
The hash->address mapping does not exist in our index. If the index is up-to-date, this likely means
that the contract was created by an internal transaction. Skip this contract as right now we don't If the index is up-to-date, this likely means that
have a good solution for this. the contract was created by an internal transaction.
Skip this contract as right now we don't have a good
solution for this.
""" """
continue continue

@ -1,4 +1,5 @@
"""This module contains the ETH_DB class, which the base database used by pyethereum.""" """This module contains the ETH_DB class, which the base database used by
pyethereum."""
import plyvel import plyvel
from ethereum.db import BaseDB from ethereum.db import BaseDB
@ -10,19 +11,13 @@ class ETH_DB(BaseDB):
self.db = plyvel.DB(path) self.db = plyvel.DB(path)
def get(self, key): def get(self, key):
""" """gets value for key."""
gets value for key
"""
return self.db.get(key) return self.db.get(key)
def put(self, key, value): def put(self, key, value):
""" """puts value for key."""
puts value for key
"""
self.db.put(key, value) self.db.put(key, value)
def write_batch(self): def write_batch(self):
""" """start writing a batch."""
start writing a batch
"""
return self.db.write_batch() return self.db.write_batch()

@ -48,9 +48,7 @@ STATE_DEFAULTS = {
class Account(rlp.Serializable): class Account(rlp.Serializable):
""" """adjusted account from ethereum.state."""
adjusted account from ethereum.state
"""
fields = [ fields = [
("nonce", big_endian_int), ("nonce", big_endian_int),
@ -82,14 +80,11 @@ class Account(rlp.Serializable):
@property @property
def code(self): def code(self):
""" """code rlp data."""
code rlp data
"""
return self.db.get(self.code_hash) return self.db.get(self.code_hash)
def get_storage_data(self, key): def get_storage_data(self, key):
""" """get storage data.
get storage data
:param key: :param key:
:return: :return:
@ -103,8 +98,8 @@ class Account(rlp.Serializable):
@classmethod @classmethod
def blank_account(cls, db, addr, initial_nonce=0): def blank_account(cls, db, addr, initial_nonce=0):
""" """creates a blank account.
creates a blank account
:param db: :param db:
:param addr: :param addr:
:param initial_nonce: :param initial_nonce:
@ -116,17 +111,15 @@ class Account(rlp.Serializable):
return o return o
def is_blank(self): def is_blank(self):
""" """checks if is a blank account.
checks if is a blank account
:return: :return:
""" """
return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH
class State: class State:
""" """adjusted state from ethereum.state."""
adjusted state from ethereum.state
"""
def __init__(self, db, root): def __init__(self, db, root):
""" """
@ -141,7 +134,8 @@ class State:
self.cache = {} self.cache = {}
def get_and_cache_account(self, addr): def get_and_cache_account(self, addr):
"""Gets and caches an account for an addres, creates blank if not found """Gets and caches an account for an addres, creates blank if not
found.
:param addr: :param addr:
:return: :return:
@ -165,9 +159,7 @@ class State:
return o return o
def get_all_accounts(self): def get_all_accounts(self):
""" """iterates through trie to and yields non-blank leafs as accounts."""
iterates through trie to and yields non-blank leafs as accounts
"""
for address_hash, rlpdata in self.secure_trie.trie.iter_branch(): for address_hash, rlpdata in self.secure_trie.trie.iter_branch():
if rlpdata != trie.BLANK_NODE: if rlpdata != trie.BLANK_NODE:
yield rlp.decode(rlpdata, Account, db=self.db, address=address_hash) yield rlp.decode(rlpdata, Account, db=self.db, address=address_hash)

@ -1,4 +1,5 @@
"""This file contains constants used used by the Ethereum JSON RPC interface.""" """This file contains constants used used by the Ethereum JSON RPC
interface."""
BLOCK_TAG_EARLIEST = "earliest" BLOCK_TAG_EARLIEST = "earliest"
BLOCK_TAG_LATEST = "latest" BLOCK_TAG_LATEST = "latest"
BLOCK_TAG_PENDING = "pending" BLOCK_TAG_PENDING = "pending"

@ -8,24 +8,28 @@ class EthJsonRpcError(Exception):
class ConnectionError(EthJsonRpcError): class ConnectionError(EthJsonRpcError):
"""An RPC exception denoting there was an error in connecting to the RPC instance.""" """An RPC exception denoting there was an error in connecting to the RPC
instance."""
pass pass
class BadStatusCodeError(EthJsonRpcError): class BadStatusCodeError(EthJsonRpcError):
"""An RPC exception denoting a bad status code returned by the RPC instance.""" """An RPC exception denoting a bad status code returned by the RPC
instance."""
pass pass
class BadJsonError(EthJsonRpcError): class BadJsonError(EthJsonRpcError):
"""An RPC exception denoting that the RPC instance returned a bad JSON object.""" """An RPC exception denoting that the RPC instance returned a bad JSON
object."""
pass pass
class BadResponseError(EthJsonRpcError): class BadResponseError(EthJsonRpcError):
"""An RPC exception denoting that the RPC instance returned a bad response.""" """An RPC exception denoting that the RPC instance returned a bad
response."""
pass pass

@ -1,10 +1,10 @@
"""This module contains various utility functions regarding the RPC data format and validation.""" """This module contains various utility functions regarding the RPC data format
and validation."""
from .constants import BLOCK_TAGS from .constants import BLOCK_TAGS
def hex_to_dec(x): def hex_to_dec(x):
""" """Convert hex to decimal.
Convert hex to decimal
:param x: :param x:
:return: :return:
@ -13,9 +13,8 @@ def hex_to_dec(x):
def clean_hex(d): def clean_hex(d):
""" """Convert decimal to hex and remove the "L" suffix that is appended to
Convert decimal to hex and remove the "L" suffix that is appended to large large numbers.
numbers
:param d: :param d:
:return: :return:
@ -38,7 +37,7 @@ def validate_block(block):
def wei_to_ether(wei): def wei_to_ether(wei):
"""Convert wei to ether """Convert wei to ether.
:param wei: :param wei:
:return: :return:
@ -47,7 +46,7 @@ def wei_to_ether(wei):
def ether_to_wei(ether): def ether_to_wei(ether):
"""Convert ether to wei """Convert ether to wei.
:param ether: :param ether:
:return: :return:

@ -1,4 +1,5 @@
"""This module contains various utility functions regarding unit conversion and solc integration.""" """This module contains various utility functions regarding unit conversion and
solc integration."""
from ethereum.abi import encode_abi, encode_int from ethereum.abi import encode_abi, encode_int
from ethereum.utils import zpad from ethereum.utils import zpad
from ethereum.abi import method_id from ethereum.abi import method_id

@ -14,24 +14,28 @@ class CompilerError(MythrilBaseException):
class UnsatError(MythrilBaseException): class UnsatError(MythrilBaseException):
"""A Mythril exception denoting the unsatisfiability of a series of constraints.""" """A Mythril exception denoting the unsatisfiability of a series of
constraints."""
pass pass
class NoContractFoundError(MythrilBaseException): class NoContractFoundError(MythrilBaseException):
"""A Mythril exception denoting that a given contract file was not found.""" """A Mythril exception denoting that a given contract file was not
found."""
pass pass
class CriticalError(MythrilBaseException): class CriticalError(MythrilBaseException):
"""A Mythril exception denoting an unknown critical error has been encountered.""" """A Mythril exception denoting an unknown critical error has been
encountered."""
pass pass
class AddressNotFoundError(MythrilBaseException): class AddressNotFoundError(MythrilBaseException):
"""A Mythril exception denoting the given smart contract address was not found.""" """A Mythril exception denoting the given smart contract address was not
found."""
pass pass

@ -21,9 +21,7 @@ PY3 = sys.version_info >= (3,)
# Reset terminal colors at exit # Reset terminal colors at exit
def reset(): def reset():
""" """"""
"""
sys.stdout.write("\x1b[0m") sys.stdout.write("\x1b[0m")
sys.stdout.flush() sys.stdout.flush()
@ -53,7 +51,7 @@ COLOR_ANSI = (
class LolCat(object): class LolCat(object):
"""Cats lel""" """Cats lel."""
def __init__(self, mode=256, output=sys.stdout): def __init__(self, mode=256, output=sys.stdout):
self.mode = mode self.mode = mode
@ -193,9 +191,7 @@ class LolCat(object):
def detect_mode(term_hint="xterm-256color"): def detect_mode(term_hint="xterm-256color"):
""" """Poor-mans color mode detection."""
Poor-mans color mode detection.
"""
if "ANSICON" in os.environ: if "ANSICON" in os.environ:
return 16 return 16
elif os.environ.get("ConEmuANSI", "OFF") == "ON": elif os.environ.get("ConEmuANSI", "OFF") == "ON":

@ -1,7 +1,6 @@
""" """This module contains the business logic used by Instruction in
This module contains the business logic used by Instruction in instructions.py instructions.py to get the necessary elements from the stack and determine the
to get the necessary elements from the stack and determine the parameters for the new global state. parameters for the new global state."""
"""
import logging import logging
from typing import Union from typing import Union
@ -28,9 +27,9 @@ log = logging.getLogger(__name__)
def get_call_parameters( def get_call_parameters(
global_state: GlobalState, dynamic_loader: DynLoader, with_value=False global_state: GlobalState, dynamic_loader: DynLoader, with_value=False
): ):
""" """Gets call parameters from global state Pops the values from the stack
Gets call parameters from global state and determines output parameters.
Pops the values from the stack and determines output parameters
:param global_state: state to look in :param global_state: state to look in
:param dynamic_loader: dynamic loader to use :param dynamic_loader: dynamic loader to use
:param with_value: whether to pop the value argument from the stack :param with_value: whether to pop the value argument from the stack
@ -71,8 +70,8 @@ def get_callee_address(
dynamic_loader: DynLoader, dynamic_loader: DynLoader,
symbolic_to_address: Expression, symbolic_to_address: Expression,
): ):
""" """Gets the address of the callee.
Gets the address of the callee
:param global_state: state to look in :param global_state: state to look in
:param dynamic_loader: dynamic loader to use :param dynamic_loader: dynamic loader to use
:param symbolic_to_address: The (symbolic) callee address :param symbolic_to_address: The (symbolic) callee address
@ -114,8 +113,8 @@ def get_callee_address(
def get_callee_account( def get_callee_account(
global_state: GlobalState, callee_address: str, dynamic_loader: DynLoader global_state: GlobalState, callee_address: str, dynamic_loader: DynLoader
): ):
""" """Gets the callees account from the global_state.
Gets the callees account from the global_state
:param global_state: state to look in :param global_state: state to look in
:param callee_address: address of the callee :param callee_address: address of the callee
:param dynamic_loader: dynamic loader to use :param dynamic_loader: dynamic loader to use
@ -158,8 +157,8 @@ def get_call_data(
memory_start: Union[int, Expression], memory_start: Union[int, Expression],
memory_size: Union[int, Expression], memory_size: Union[int, Expression],
): ):
""" """Gets call_data from the global_state.
Gets call_data from the global_state
:param global_state: state to look in :param global_state: state to look in
:param memory_start: Start index :param memory_start: Start index
:param memory_size: Size :param memory_size: Size

@ -1,4 +1,4 @@
"""This module """ """This module."""
from flags import Flags from flags import Flags
from enum import Enum from enum import Enum
from typing import Dict from typing import Dict

@ -1,4 +1,5 @@
"""This module contains functions for dynamic gas calculation and a gas cost table.""" """This module contains functions for dynamic gas calculation and a gas cost
table."""
from ethereum import opcodes from ethereum import opcodes
from ethereum.utils import ceil32 from ethereum.utils import ceil32

@ -1,4 +1,5 @@
"""This module contains a representation class for EVM instructions and transitions between them.""" """This module contains a representation class for EVM instructions and
transitions between them."""
import binascii import binascii
import logging import logging
@ -63,9 +64,10 @@ keccak_function_manager = KeccakFunctionManager()
class StateTransition(object): class StateTransition(object):
"""Decorator that handles global state copy and original return. """Decorator that handles global state copy and original return.
This decorator calls the decorated instruction mutator function on a copy of the state that This decorator calls the decorated instruction mutator function on a
is passed to it. After the call, the resulting new states' program counter is automatically copy of the state that is passed to it. After the call, the
incremented if `increment_pc=True`. resulting new states' program counter is automatically incremented
if `increment_pc=True`.
""" """
def __init__(self, increment_pc=True, enable_gas=True): def __init__(self, increment_pc=True, enable_gas=True):
@ -153,9 +155,8 @@ class StateTransition(object):
class Instruction: class Instruction:
""" """Instruction class is used to mutate a state according to the current
Instruction class is used to mutate a state according to the current instruction instruction."""
"""
def __init__(self, op_code: str, dynamic_loader: DynLoader): def __init__(self, op_code: str, dynamic_loader: DynLoader):
""" """
@ -167,7 +168,7 @@ class Instruction:
self.op_code = op_code.upper() self.op_code = op_code.upper()
def evaluate(self, global_state: GlobalState, post=False) -> List[GlobalState]: def evaluate(self, global_state: GlobalState, post=False) -> List[GlobalState]:
""" Performs the mutation for this instruction """Performs the mutation for this instruction.
:param global_state: :param global_state:
:param post: :param post:

@ -1,4 +1,5 @@
"""This module contains a function manager to deal with symbolic Keccak values.""" """This module contains a function manager to deal with symbolic Keccak
values."""
from mythril.laser.smt import Expression from mythril.laser.smt import Expression
@ -6,9 +7,7 @@ class KeccakFunctionManager:
"""A keccak function manager for symbolic expressions.""" """A keccak function manager for symbolic expressions."""
def __init__(self): def __init__(self):
""" """"""
"""
self.keccak_expression_mapping = {} self.keccak_expression_mapping = {}
def is_keccak(self, expression: Expression) -> bool: def is_keccak(self, expression: Expression) -> bool:

@ -124,7 +124,7 @@ def identity(data: Union[bytes, str, List[int]]) -> bytes:
def native_contracts(address: int, data: BaseCalldata): def native_contracts(address: int, data: BaseCalldata):
"""Takes integer address 1, 2, 3, 4 """Takes integer address 1, 2, 3, 4.
:param address: :param address:
:param data: :param data:

@ -12,13 +12,11 @@ from mythril.disassembler.disassembly import Disassembly
class Storage: class Storage:
""" """Storage class represents the storage of an Account."""
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):
""" """Constructor for Storage.
Constructor for Storage
:param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic :param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic
""" """
self._storage = {} self._storage = {}
@ -62,9 +60,7 @@ class Storage:
class Account: class Account:
""" """Account class representing ethereum accounts."""
Account class representing ethereum accounts
"""
def __init__( def __init__(
self, self,
@ -75,8 +71,8 @@ class Account:
concrete_storage=False, concrete_storage=False,
dynamic_loader=None, dynamic_loader=None,
): ):
""" """Constructor for account.
Constructor for account
:param address: Address of the account :param address: Address of the account
:param code: The contract code of the account :param code: The contract code of the account
:param contract_name: The name associated with the account :param contract_name: The name associated with the account

@ -1,20 +1,21 @@
"""This module includes classes used for annotating trace information. """This module includes classes used for annotating trace information.
This includes the base StateAnnotation class, as well as an adaption, which will not be This includes the base StateAnnotation class, as well as an adaption,
copied on every new state. which will not be copied on every new state.
""" """
class StateAnnotation: class StateAnnotation:
""" """The StateAnnotation class is used to persist information over traces.
The StateAnnotation class is used to persist information over traces. This allows modules to reason about traces
without the need to traverse the state space themselves. This allows modules to reason about traces without the need to
traverse the state space themselves.
""" """
@property @property
def persist_to_world_state(self) -> bool: def persist_to_world_state(self) -> bool:
""" """If this function returns true then laser will also annotate the
If this function returns true then laser will also annotate the world state. world state.
If you want annotations to persist through different user initiated message call transactions If you want annotations to persist through different user initiated message call transactions
then this should be enabled. then this should be enabled.
@ -25,10 +26,11 @@ class StateAnnotation:
class NoCopyAnnotation(StateAnnotation): class NoCopyAnnotation(StateAnnotation):
""" """This class provides a base annotation class for annotations that
This class provides a base annotation class for annotations that shouldn't be copied on every new state. shouldn't be copied on every new state.
Rather the same object should be propagated.
This is very useful if you are looking to analyze a property over multiple substates Rather the same object should be propagated. This is very useful if
you are looking to analyze a property over multiple substates
""" """
def __copy__(self): def __copy__(self):

@ -18,10 +18,8 @@ class CalldataType(Enum):
class BaseCalldata: class BaseCalldata:
""" """Base calldata class This represents the calldata provided when sending a
Base calldata class transaction to a contract."""
This represents the calldata provided when sending a transaction to a contract
"""
def __init__(self, tx_id): def __init__(self, tx_id):
""" """
@ -42,7 +40,7 @@ class BaseCalldata:
return result return result
def get_word_at(self, offset: int) -> Expression: def get_word_at(self, offset: int) -> Expression:
""" Gets word at offset """Gets word at offset.
:param offset: :param offset:
:return: :return:
@ -94,14 +92,14 @@ class BaseCalldata:
@property @property
def size(self) -> Union[Expression, int]: def size(self) -> Union[Expression, int]:
""" Returns the exact size of this calldata, this is not normalized """Returns the exact size of this calldata, this is not normalized.
:return: unnormalized call data size :return: unnormalized call data size
""" """
raise NotImplementedError() raise NotImplementedError()
def concrete(self, model: Model) -> list: def concrete(self, model: Model) -> list:
""" Returns a concrete version of the calldata using the provided model """Returns a concrete version of the calldata using the provided model.
:param model: :param model:
""" """
@ -112,8 +110,7 @@ class ConcreteCalldata(BaseCalldata):
"""A concrete call data representation.""" """A concrete call data representation."""
def __init__(self, tx_id: int, calldata: list): def __init__(self, tx_id: int, calldata: list):
""" """Initializes the ConcreteCalldata object.
Initializes the ConcreteCalldata object.
:param tx_id: Id of the transaction that the calldata is for. :param tx_id: Id of the transaction that the calldata is for.
:param calldata: The concrete calldata content :param calldata: The concrete calldata content
@ -257,8 +254,8 @@ class BasicSymbolicCalldata(BaseCalldata):
"""A basic class representing symbolic call data.""" """A basic class representing symbolic call data."""
def __init__(self, tx_id: int): def __init__(self, tx_id: int):
""" """Initializes the SymbolicCalldata object.
Initializes the SymbolicCalldata object
:param tx_id: Id of the transaction that the calldata is for. :param tx_id: Id of the transaction that the calldata is for.
""" """
self._reads = [] self._reads = []

@ -1,10 +1,12 @@
"""This module contains the class used to represent state-change constraints in the call graph.""" """This module contains the class used to represent state-change constraints in
the call graph."""
class Constraints(list): class Constraints(list):
""" """This class should maintain a solver and it's constraints, This class
This class should maintain a solver and it's constraints, This class tries to make the Constraints() object tries to make the Constraints() object as a simple list of constraints with
as a simple list of constraints with some background processing. some background processing.
TODO: add the solver to this class after callback refactor TODO: add the solver to this class after callback refactor
""" """

@ -1,4 +1,5 @@
"""This module contains the representation for an execution state's environment.""" """This module contains the representation for an execution state's
environment."""
from typing import Dict from typing import Dict
from z3 import ExprRef, BitVecVal from z3 import ExprRef, BitVecVal
@ -9,7 +10,8 @@ from mythril.laser.ethereum.state.calldata import CalldataType, BaseCalldata
class Environment: class Environment:
"""The environment class represents the current execution environment for the symbolic executor.""" """The environment class represents the current execution environment for
the symbolic executor."""
def __init__( def __init__(
self, self,

@ -12,9 +12,7 @@ from mythril.laser.ethereum.state.annotation import StateAnnotation
class GlobalState: class GlobalState:
""" """GlobalState represents the current globalstate."""
GlobalState represents the current globalstate
"""
def __init__( def __init__(
self, self,
@ -26,7 +24,7 @@ class GlobalState:
last_return_data=None, last_return_data=None,
annotations=None, annotations=None,
): ):
""" Constructor for GlobalState """Constructor for GlobalState.
:param world_state: :param world_state:
:param environment: :param environment:
@ -76,7 +74,7 @@ class GlobalState:
# TODO: remove this, as two instructions are confusing # TODO: remove this, as two instructions are confusing
def get_current_instruction(self) -> Dict: def get_current_instruction(self) -> Dict:
""" Gets the current instruction for this GlobalState """Gets the current instruction for this GlobalState.
:return: :return:
""" """
@ -135,9 +133,9 @@ class GlobalState:
return self._annotations return self._annotations
def get_annotations(self, annotation_type: type) -> Iterable[StateAnnotation]: def get_annotations(self, annotation_type: type) -> Iterable[StateAnnotation]:
""" """Filters annotations for the queried annotation type. Designed
Filters annotations for the queried annotation type. Designed particularly for particularly for modules with annotations:
modules with annotations: globalstate.get_annotations(MySpecificModuleAnnotation) globalstate.get_annotations(MySpecificModuleAnnotation)
:param annotation_type: The type to filter annotations for :param annotation_type: The type to filter annotations for
:return: filter of matching annotations :return: filter of matching annotations

@ -1,4 +1,5 @@
"""This module contains a representation of the EVM's machine state and its stack.""" """This module contains a representation of the EVM's machine state and its
stack."""
from copy import copy from copy import copy
from typing import Union, Any, List, Dict from typing import Union, Any, List, Dict
@ -15,9 +16,7 @@ from mythril.laser.ethereum.state.memory import Memory
class MachineStack(list): class MachineStack(list):
""" """Defines EVM stack, overrides the default list to handle overflows."""
Defines EVM stack, overrides the default list to handle overflows
"""
STACK_LIMIT = 1024 STACK_LIMIT = 1024
@ -68,24 +67,23 @@ class MachineStack(list):
) )
def __add__(self, other): def __add__(self, other):
"""Implement list concatenation if needed """Implement list concatenation if needed.
:param other: :param other:
""" """
raise NotImplementedError("Implement this if needed") raise NotImplementedError("Implement this if needed")
def __iadd__(self, other): def __iadd__(self, other):
""" """Implement list concatenation if needed.
Implement list concatenation if needed
:param other: :param other:
""" """
raise NotImplementedError("Implement this if needed") raise NotImplementedError("Implement this if needed")
class MachineState: class MachineState:
""" """MachineState represents current machine state also referenced to as
MachineState represents current machine state also referenced to as \mu \mu."""
"""
def __init__( def __init__(
self, self,
@ -98,7 +96,7 @@ class MachineState:
max_gas_used=0, max_gas_used=0,
min_gas_used=0, min_gas_used=0,
): ):
""" Constructor for machineState """Constructor for machineState.
:param gas_limit: :param gas_limit:
:param pc: :param pc:
@ -153,7 +151,7 @@ class MachineState:
raise OutOfGasException() raise OutOfGasException()
def mem_extend(self, start: int, size: int) -> None: def mem_extend(self, start: int, size: int) -> None:
"""Extends the memory of this machine state """Extends the memory of this machine state.
:param start: Start of memory extension :param start: Start of memory extension
:param size: Size of memory extension :param size: Size of memory extension
@ -167,7 +165,7 @@ class MachineState:
self.memory.extend(m_extend) self.memory.extend(m_extend)
def memory_write(self, offset: int, data: List[int]) -> None: def memory_write(self, offset: int, data: List[int]) -> None:
""" Writes data to memory starting at offset """Writes data to memory starting at offset.
:param offset: :param offset:
:param data: :param data:
@ -176,7 +174,7 @@ class MachineState:
self.memory[offset : offset + len(data)] = data self.memory[offset : offset + len(data)] = data
def pop(self, amount=1) -> Union[BitVec, List[BitVec]]: def pop(self, amount=1) -> Union[BitVec, List[BitVec]]:
""" Pops amount elements from the stack """Pops amount elements from the stack.
:param amount: :param amount:
:return: :return:

@ -17,9 +17,7 @@ class Memory:
"""A class representing contract memory with random access.""" """A class representing contract memory with random access."""
def __init__(self): def __init__(self):
""" """"""
"""
self._memory = [] self._memory = []
def __len__(self): def __len__(self):
@ -37,7 +35,7 @@ class Memory:
self._memory.extend(bytearray(size)) self._memory.extend(bytearray(size))
def get_word_at(self, index: int) -> Union[int, BitVec]: def get_word_at(self, index: int) -> Union[int, BitVec]:
"""Access a word from a specified memory index """Access a word from a specified memory index.
:param index: integer representing the index to access :param index: integer representing the index to access
:return: 32 byte word at the specified index :return: 32 byte word at the specified index

@ -8,15 +8,13 @@ from mythril.laser.ethereum.state.annotation import StateAnnotation
class WorldState: class WorldState:
""" """The WorldState class represents the world state as described in the
The WorldState class represents the world state as described in the yellow paper yellow paper."""
"""
def __init__( def __init__(
self, transaction_sequence=None, annotations: List[StateAnnotation] = None self, transaction_sequence=None, annotations: List[StateAnnotation] = None
) -> None: ) -> None:
""" """Constructor for the world state. Initializes the accounts record.
Constructor for the world state. Initializes the accounts record
:param transaction_sequence: :param transaction_sequence:
:param annotations: :param annotations:
@ -27,8 +25,7 @@ class WorldState:
self._annotations = annotations or [] self._annotations = annotations or []
def __getitem__(self, item: str) -> Account: def __getitem__(self, item: str) -> Account:
""" """Gets an account from the worldstate using item as key.
Gets an account from the worldstate using item as key
:param item: Address of the account to get :param item: Address of the account to get
:return: Account associated with the address :return: Account associated with the address
@ -52,8 +49,7 @@ class WorldState:
def create_account( def create_account(
self, balance=0, address=None, concrete_storage=False, dynamic_loader=None self, balance=0, address=None, concrete_storage=False, dynamic_loader=None
) -> Account: ) -> Account:
""" """Create non-contract account.
Create non-contract account
:param address: The account's address :param address: The account's address
:param balance: Initial balance for the account :param balance: Initial balance for the account
@ -72,9 +68,9 @@ class WorldState:
return new_account return new_account
def create_initialized_contract_account(self, contract_code, storage) -> None: def create_initialized_contract_account(self, contract_code, storage) -> None:
""" """Creates a new contract account, based on the contract code and
Creates a new contract account, based on the contract code and storage provided storage provided The contract code only includes the runtime contract
The contract code only includes the runtime contract bytecode bytecode.
:param contract_code: Runtime bytecode for the contract :param contract_code: Runtime bytecode for the contract
:param storage: Initial storage for the contract :param storage: Initial storage for the contract
@ -103,9 +99,9 @@ class WorldState:
return self._annotations return self._annotations
def get_annotations(self, annotation_type: type) -> Iterator[StateAnnotation]: def get_annotations(self, annotation_type: type) -> Iterator[StateAnnotation]:
""" """Filters annotations for the queried annotation type. Designed
Filters annotations for the queried annotation type. Designed particularly for particularly for modules with annotations:
modules with annotations: worldstate.get_annotations(MySpecificModuleAnnotation) worldstate.get_annotations(MySpecificModuleAnnotation)
:param annotation_type: The type to filter annotations for :param annotation_type: The type to filter annotations for
:return: filter of matching annotations :return: filter of matching annotations
@ -113,7 +109,7 @@ class WorldState:
return filter(lambda x: isinstance(x, annotation_type), self.annotations) return filter(lambda x: isinstance(x, annotation_type), self.annotations)
def _generate_new_address(self) -> str: def _generate_new_address(self) -> str:
""" Generates a new address for the global state """Generates a new address for the global state.
:return: :return:
""" """

@ -2,9 +2,7 @@ from abc import ABC, abstractmethod
class BasicSearchStrategy(ABC): class BasicSearchStrategy(ABC):
""" """"""
"""
__slots__ = "work_list", "max_depth" __slots__ = "work_list", "max_depth"
@ -17,9 +15,7 @@ class BasicSearchStrategy(ABC):
@abstractmethod @abstractmethod
def get_strategic_global_state(self): def get_strategic_global_state(self):
""" """"""
"""
raise NotImplementedError("Must be implemented by a subclass") raise NotImplementedError("Must be implemented by a subclass")
def __next__(self): def __next__(self):

@ -1,6 +1,4 @@
""" """This module implements basic symbolic execution search strategies."""
This module implements basic symbolic execution search strategies
"""
from mythril.laser.ethereum.state.global_state import GlobalState from mythril.laser.ethereum.state.global_state import GlobalState
from typing import List from typing import List
from random import randrange from random import randrange
@ -16,8 +14,8 @@ except ImportError:
from bisect import bisect from bisect import bisect
def choices(population, weights=None): def choices(population, weights=None):
""" """Returns a random element out of the population based on weight.
Returns a random element out of the population based on weight.
If the relative weights or cumulative weights are not specified, If the relative weights or cumulative weights are not specified,
the selections are made with equal probability. the selections are made with equal probability.
""" """
@ -32,9 +30,9 @@ except ImportError:
class DepthFirstSearchStrategy(BasicSearchStrategy): class DepthFirstSearchStrategy(BasicSearchStrategy):
""" """Implements a depth first search strategy I.E.
Implements a depth first search strategy
I.E. Follow one path to a leaf, and then continue to the next one Follow one path to a leaf, and then continue to the next one
""" """
def get_strategic_global_state(self) -> GlobalState: def get_strategic_global_state(self) -> GlobalState:
@ -46,9 +44,9 @@ class DepthFirstSearchStrategy(BasicSearchStrategy):
class BreadthFirstSearchStrategy(BasicSearchStrategy): class BreadthFirstSearchStrategy(BasicSearchStrategy):
""" """Implements a breadth first search strategy I.E.
Implements a breadth first search strategy
I.E. Execute all states of a "level" before continuing Execute all states of a "level" before continuing
""" """
def get_strategic_global_state(self) -> GlobalState: def get_strategic_global_state(self) -> GlobalState:
@ -60,9 +58,7 @@ class BreadthFirstSearchStrategy(BasicSearchStrategy):
class ReturnRandomNaivelyStrategy(BasicSearchStrategy): class ReturnRandomNaivelyStrategy(BasicSearchStrategy):
""" """chooses a random state from the worklist with equal likelihood."""
chooses a random state from the worklist with equal likelihood
"""
def get_strategic_global_state(self) -> GlobalState: def get_strategic_global_state(self) -> GlobalState:
""" """
@ -76,9 +72,8 @@ class ReturnRandomNaivelyStrategy(BasicSearchStrategy):
class ReturnWeightedRandomStrategy(BasicSearchStrategy): class ReturnWeightedRandomStrategy(BasicSearchStrategy):
""" """chooses a random state from the worklist with likelihood based on
chooses a random state from the worklist with likelihood based on inverse proportion to depth inverse proportion to depth."""
"""
def get_strategic_global_state(self) -> GlobalState: def get_strategic_global_state(self) -> GlobalState:
""" """

@ -36,10 +36,12 @@ class SVMError(Exception):
class LaserEVM: class LaserEVM:
"""The LASER EVM. """The LASER EVM.
Just as Mithril had to be mined at great efforts to provide the Dwarves with their exceptional armour, Just as Mithril had to be mined at great efforts to provide the
LASER stands at the heart of Mythril, digging deep in the depths of call graphs, unearthing the most Dwarves with their exceptional armour, LASER stands at the heart of
precious symbolic call data, that is then hand-forged into beautiful and strong security issues by Mythril, digging deep in the depths of call graphs, unearthing the
the experienced smiths we call detection modules. It is truly a magnificent symbiosis. most precious symbolic call data, that is then hand-forged into
beautiful and strong security issues by the experienced smiths we
call detection modules. It is truly a magnificent symbiosis.
""" """
def __init__( def __init__(
@ -166,8 +168,9 @@ class LaserEVM:
log.info("Achieved {:.2f}% coverage for code: {}".format(cov, code)) log.info("Achieved {:.2f}% coverage for code: {}".format(cov, code))
def _execute_transactions(self, address): def _execute_transactions(self, address):
""" """This function executes multiple transactions on the address based on
This function executes multiple transactions on the address based on the coverage the coverage.
:param address: Address of the contract :param address: Address of the contract
:return: :return:
""" """
@ -188,7 +191,8 @@ class LaserEVM:
) )
def _get_covered_instructions(self) -> int: def _get_covered_instructions(self) -> int:
""" Gets the total number of covered instructions for all accounts in the svm """Gets the total number of covered instructions for all accounts in
the svm.
:return: :return:
""" """

@ -13,20 +13,19 @@ log = logging.getLogger(__name__)
class TaintRecord: class TaintRecord:
""" """TaintRecord contains tainting information for a specific (state, node)
TaintRecord contains tainting information for a specific (state, node) the information specifies the taint status before executing the operation
the information specifies the taint status before executing the operation belonging to the state belonging to the state."""
"""
def __init__(self): def __init__(self):
""" Builds a taint record """ """Builds a taint record."""
self.stack = [] self.stack = []
self.memory = {} self.memory = {}
self.storage = {} self.storage = {}
self.states = [] self.states = []
def stack_tainted(self, index: int) -> Union[bool, None]: def stack_tainted(self, index: int) -> Union[bool, None]:
""" Returns taint value of stack element at index """Returns taint value of stack element at index.
:param index: :param index:
:return: :return:
@ -36,7 +35,7 @@ class TaintRecord:
return None return None
def memory_tainted(self, index: int) -> bool: def memory_tainted(self, index: int) -> bool:
""" Returns taint value of memory element at index """Returns taint value of memory element at index.
:param index: :param index:
:return: :return:
@ -46,7 +45,7 @@ class TaintRecord:
return False return False
def storage_tainted(self, index: int) -> bool: def storage_tainted(self, index: int) -> bool:
""" Returns taint value of storage element at index """Returns taint value of storage element at index.
:param index: :param index:
:return: :return:
@ -56,14 +55,14 @@ class TaintRecord:
return False return False
def add_state(self, state: GlobalState) -> None: def add_state(self, state: GlobalState) -> None:
""" Adds state with this taint record """Adds state with this taint record.
:param state: :param state:
""" """
self.states.append(state) self.states.append(state)
def clone(self) -> "TaintRecord": def clone(self) -> "TaintRecord":
""" Clones this record """Clones this record.
:return: :return:
""" """
@ -75,14 +74,15 @@ class TaintRecord:
class TaintResult: class TaintResult:
""" Taint analysis result obtained after having ran the taint runner""" """Taint analysis result obtained after having ran the taint runner."""
def __init__(self): def __init__(self):
"""Create a new tains result.""" """Create a new tains result."""
self.records = [] self.records = []
def check(self, state: GlobalState, stack_index: int) -> Union[bool, None]: def check(self, state: GlobalState, stack_index: int) -> Union[bool, None]:
"""Checks if stack variable is tainted, before executing the instruction """Checks if stack variable is tainted, before executing the
instruction.
:param state: state to check variable in :param state: state to check variable in
:param stack_index: index of stack variable :param stack_index: index of stack variable
@ -94,14 +94,14 @@ class TaintResult:
return record.stack_tainted(stack_index) return record.stack_tainted(stack_index)
def add_records(self, records: List[TaintRecord]) -> None: def add_records(self, records: List[TaintRecord]) -> None:
""" Adds records to this taint result """Adds records to this taint result.
:param records: :param records:
""" """
self.records += records self.records += records
def _try_get_record(self, state: GlobalState) -> Union[TaintRecord, None]: def _try_get_record(self, state: GlobalState) -> Union[TaintRecord, None]:
""" Finds record belonging to the state """Finds record belonging to the state.
:param state: :param state:
:return: :return:
@ -113,16 +113,15 @@ class TaintResult:
class TaintRunner: class TaintRunner:
""" """Taint runner, is able to run taint analysis on symbolic execution
Taint runner, is able to run taint analysis on symbolic execution result result."""
"""
@staticmethod @staticmethod
def execute( def execute(
statespace: SymExecWrapper, node: Node, state: GlobalState, initial_stack=None statespace: SymExecWrapper, node: Node, state: GlobalState, initial_stack=None
) -> TaintResult: ) -> TaintResult:
""" """Runs taint analysis on the statespace.
Runs taint analysis on the statespace
:param initial_stack: :param initial_stack:
:param statespace: symbolic statespace to run taint analysis on :param statespace: symbolic statespace to run taint analysis on
:param node: taint introduction node :param node: taint introduction node
@ -196,8 +195,8 @@ class TaintRunner:
def execute_node( def execute_node(
node: Node, last_record: TaintRecord, state_index=0 node: Node, last_record: TaintRecord, state_index=0
) -> List[TaintRecord]: ) -> List[TaintRecord]:
""" """Runs taint analysis on a given node.
Runs taint analysis on a given node
:param node: node to analyse :param node: node to analyse
:param last_record: last taint record to work from :param last_record: last taint record to work from
:param state_index: state index to start from :param state_index: state index to start from

@ -1,4 +1,5 @@
"""This module contains functions to set up and execute concolic message calls.""" """This module contains functions to set up and execute concolic message
calls."""
from typing import List, Union from typing import List, Union
from mythril.disassembler.disassembly import Disassembly from mythril.disassembler.disassembly import Disassembly
@ -23,7 +24,7 @@ def execute_message_call(
value, value,
track_gas=False, track_gas=False,
) -> Union[None, List[GlobalState]]: ) -> Union[None, List[GlobalState]]:
""" Execute a message call transaction from all open states. """Execute a message call transaction from all open states.
:param laser_evm: :param laser_evm:
:param callee_address: :param callee_address:
@ -63,7 +64,7 @@ def execute_message_call(
def _setup_global_state_for_execution(laser_evm, transaction) -> None: def _setup_global_state_for_execution(laser_evm, transaction) -> None:
""" Set up global state and cfg for a transactions execution. """Set up global state and cfg for a transactions execution.
:param laser_evm: :param laser_evm:
:param transaction: :param transaction:

@ -1,4 +1,5 @@
"""This module contains functions setting up and executing transactions with symbolic values.""" """This module contains functions setting up and executing transactions with
symbolic values."""
import logging import logging
@ -24,7 +25,7 @@ ATTACKER_ADDRESS = 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF
def execute_message_call(laser_evm, callee_address: str) -> None: def execute_message_call(laser_evm, callee_address: str) -> None:
""" Executes a message call transaction from all open states """Executes a message call transaction from all open states.
:param laser_evm: :param laser_evm:
:param callee_address: :param callee_address:
@ -65,7 +66,7 @@ def execute_message_call(laser_evm, callee_address: str) -> None:
def execute_contract_creation( def execute_contract_creation(
laser_evm, contract_initialization_code, contract_name=None laser_evm, contract_initialization_code, contract_name=None
) -> Account: ) -> Account:
""" Executes a contract creation transaction from all open states """Executes a contract creation transaction from all open states.
:param laser_evm: :param laser_evm:
:param contract_initialization_code: :param contract_initialization_code:
@ -110,7 +111,7 @@ def execute_contract_creation(
def _setup_global_state_for_execution(laser_evm, transaction) -> None: def _setup_global_state_for_execution(laser_evm, transaction) -> None:
""" Sets up global state and cfg for a transactions execution """Sets up global state and cfg for a transactions execution.
:param laser_evm: :param laser_evm:
:param transaction: :param transaction:

@ -1,4 +1,5 @@
"""This module contians the transaction models used throughout LASER's symbolic execution.""" """This module contians the transaction models used throughout LASER's symbolic
execution."""
from typing import Union from typing import Union
from mythril.disassembler.disassembly import Disassembly from mythril.disassembler.disassembly import Disassembly
@ -30,7 +31,7 @@ def get_next_transaction_id() -> int:
class TransactionEndSignal(Exception): class TransactionEndSignal(Exception):
""" Exception raised when a transaction is finalized""" """Exception raised when a transaction is finalized."""
def __init__(self, global_state: GlobalState, revert=False): def __init__(self, global_state: GlobalState, revert=False):
self.global_state = global_state self.global_state = global_state
@ -38,7 +39,7 @@ class TransactionEndSignal(Exception):
class TransactionStartSignal(Exception): class TransactionStartSignal(Exception):
""" Exception raised when a new transaction is started""" """Exception raised when a new transaction is started."""
def __init__( def __init__(
self, self,
@ -119,13 +120,13 @@ class BaseTransaction:
class MessageCallTransaction(BaseTransaction): class MessageCallTransaction(BaseTransaction):
""" Transaction object models an transaction""" """Transaction object models an transaction."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def initial_global_state(self) -> GlobalState: def initial_global_state(self) -> GlobalState:
"""Initialize the execution environment""" """Initialize the execution environment."""
environment = Environment( environment = Environment(
self.callee_account, self.callee_account,
self.caller, self.caller,
@ -152,7 +153,7 @@ class MessageCallTransaction(BaseTransaction):
class ContractCreationTransaction(BaseTransaction): class ContractCreationTransaction(BaseTransaction):
""" Transaction object models an transaction""" """Transaction object models an transaction."""
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs, init_call_data=False) super().__init__(*args, **kwargs, init_call_data=False)
@ -162,7 +163,7 @@ class ContractCreationTransaction(BaseTransaction):
) )
def initial_global_state(self) -> GlobalState: def initial_global_state(self) -> GlobalState:
"""Initialize the execution environment""" """Initialize the execution environment."""
environment = Environment( environment = Environment(
self.callee_account, self.callee_account,
self.caller, self.caller,

@ -1,4 +1,5 @@
"""This module contains various utility conversion functions and constants for LASER.""" """This module contains various utility conversion functions and constants for
LASER."""
import re import re
from mythril.laser.smt import is_false, is_true, simplify, If, BitVec, Bool, Expression from mythril.laser.smt import is_false, is_true, simplify, If, BitVec, Bool, Expression

@ -23,14 +23,13 @@ import z3
class SymbolFactory: class SymbolFactory:
""" """A symbol factory provides a default interface for all the components of
A symbol factory provides a default interface for all the components of mythril to create symbols mythril to create symbols."""
"""
@staticmethod @staticmethod
def BitVecVal(value: int, size: int, annotations=None): def BitVecVal(value: int, size: int, annotations=None):
""" """Creates a new bit vector with a concrete value.
Creates a new bit vector with a concrete value
:param value: The concrete value to set the bit vector to :param value: The concrete value to set the bit vector to
:param size: The size of the bit vector :param size: The size of the bit vector
:param annotations: The annotations to initialize the bit vector with :param annotations: The annotations to initialize the bit vector with
@ -40,8 +39,8 @@ class SymbolFactory:
@staticmethod @staticmethod
def BitVecSym(name: str, size: int, annotations=None): def BitVecSym(name: str, size: int, annotations=None):
""" """Creates a new bit vector with a symbolic value.
Creates a new bit vector with a symbolic value
:param name: The name of the symbolic bit vector :param name: The name of the symbolic bit vector
:param size: The size of the bit vector :param size: The size of the bit vector
:param annotations: The annotations to initialize the bit vector with :param annotations: The annotations to initialize the bit vector with
@ -51,38 +50,34 @@ class SymbolFactory:
class _SmtSymbolFactory(SymbolFactory): class _SmtSymbolFactory(SymbolFactory):
""" """An implementation of a SymbolFactory that creates symbols using the
An implementation of a SymbolFactory that creates symbols using classes in: mythril.laser.smt."""
the classes in: mythril.laser.smt
"""
@staticmethod @staticmethod
def BitVecVal(value: int, size: int, annotations=None): def BitVecVal(value: int, size: int, annotations=None):
""" Creates a new bit vector with a concrete value """ """Creates a new bit vector with a concrete value."""
raw = z3.BitVecVal(value, size) raw = z3.BitVecVal(value, size)
return BitVec(raw, annotations) return BitVec(raw, annotations)
@staticmethod @staticmethod
def BitVecSym(name: str, size: int, annotations=None): def BitVecSym(name: str, size: int, annotations=None):
""" Creates a new bit vector with a symbolic value """ """Creates a new bit vector with a symbolic value."""
raw = z3.BitVec(name, size) raw = z3.BitVec(name, size)
return BitVec(raw, annotations) return BitVec(raw, annotations)
class _Z3SymbolFactory(SymbolFactory): class _Z3SymbolFactory(SymbolFactory):
""" """An implementation of a SymbolFactory that directly returns z3
An implementation of a SymbolFactory that directly returns symbols."""
z3 symbols
"""
@staticmethod @staticmethod
def BitVecVal(value: int, size: int, annotations=None): def BitVecVal(value: int, size: int, annotations=None):
""" Creates a new bit vector with a concrete value """ """Creates a new bit vector with a concrete value."""
return z3.BitVecVal(value, size) return z3.BitVecVal(value, size)
@staticmethod @staticmethod
def BitVecSym(name: str, size: int, annotations=None): def BitVecSym(name: str, size: int, annotations=None):
""" Creates a new bit vector with a symbolic value """ """Creates a new bit vector with a symbolic value."""
return z3.BitVec(name, size) return z3.BitVec(name, size)

@ -1,7 +1,8 @@
"""This module contains an SMT abstraction of arrays. """This module contains an SMT abstraction of arrays.
This includes an Array class to implement basic store and set operations, as well as This includes an Array class to implement basic store and set
as a K-array, which can be initialized with default values over a certain range. operations, as well as as a K-array, which can be initialized with
default values over a certain range.
""" """
from mythril.laser.smt.bitvec import BitVec from mythril.laser.smt.bitvec import BitVec
@ -12,7 +13,7 @@ class BaseArray:
"""Base array type, which implements basic store and set operations.""" """Base array type, which implements basic store and set operations."""
def __getitem__(self, item: BitVec): def __getitem__(self, item: BitVec):
""" Gets item from the array, item can be symbolic""" """Gets item from the array, item can be symbolic."""
if isinstance(item, slice): if isinstance(item, slice):
raise ValueError( raise ValueError(
"Instance of BaseArray, does not support getitem with slices" "Instance of BaseArray, does not support getitem with slices"
@ -20,7 +21,7 @@ class BaseArray:
return BitVec(z3.Select(self.raw, item.raw)) return BitVec(z3.Select(self.raw, item.raw))
def __setitem__(self, key: BitVec, value: BitVec): def __setitem__(self, key: BitVec, value: BitVec):
""" Sets an item in the array, key can be symbolic""" """Sets an item in the array, key can be symbolic."""
self.raw = z3.Store(self.raw, key.raw, value.raw) self.raw = z3.Store(self.raw, key.raw, value.raw)
@ -28,8 +29,8 @@ class Array(BaseArray):
"""A basic symbolic array.""" """A basic symbolic array."""
def __init__(self, name: str, domain: int, value_range: int): def __init__(self, name: str, domain: int, value_range: int):
""" """Initializes a symbolic array.
Initializes a symbolic array
:param name: Name of the array :param name: Name of the array
:param domain: The domain for the array (10 -> all the values that a bv of size 10 could take) :param domain: The domain for the array (10 -> all the values that a bv of size 10 could take)
:param value_range: The range for the values in the array (10 -> all the values that a bv of size 10 could take) :param value_range: The range for the values in the array (10 -> all the values that a bv of size 10 could take)
@ -40,11 +41,12 @@ class Array(BaseArray):
class K(BaseArray): class K(BaseArray):
"""A basic symbolic array, which can be initialized with a default value.""" """A basic symbolic array, which can be initialized with a default
value."""
def __init__(self, domain: int, value_range: int, value: int): def __init__(self, domain: int, value_range: int, value: int):
""" """Initializes an array with a default value.
Initializes an array with a default value
:param domain: The domain for the array (10 -> all the values that a bv of size 10 could take) :param domain: The domain for the array (10 -> all the values that a bv of size 10 could take)
:param value_range: The range for the values in the array (10 -> all the values that a bv of size 10 could take) :param value_range: The range for the values in the array (10 -> all the values that a bv of size 10 could take)
:param value: The default value to use for this array :param value: The default value to use for this array

@ -304,7 +304,8 @@ def BVAddNoOverflow(a: Union[BitVec, int], b: Union[BitVec, int], signed: bool)
def BVMulNoOverflow(a: Union[BitVec, int], b: Union[BitVec, int], signed: bool) -> Bool: def BVMulNoOverflow(a: Union[BitVec, int], b: Union[BitVec, int], signed: bool) -> Bool:
"""Creates predicate that verifies that the multiplication doesn't overflow. """Creates predicate that verifies that the multiplication doesn't
overflow.
:param a: :param a:
:param b: :param b:

@ -1,4 +1,5 @@
"""This module provides classes for an SMT abstraction of boolean expressions.""" """This module provides classes for an SMT abstraction of boolean
expressions."""
import z3 import z3
from typing import Union from typing import Union

@ -4,7 +4,8 @@ import z3
class Expression: class Expression:
"""This is the base symbol class and maintains functionality for simplification and annotations.""" """This is the base symbol class and maintains functionality for
simplification and annotations."""
def __init__(self, raw, annotations=None): def __init__(self, raw, annotations=None):
""" """
@ -17,16 +18,16 @@ class Expression:
@property @property
def annotations(self): def annotations(self):
"""Gets the annotations for this expression """Gets the annotations for this expression.
:return: :return:
""" """
return self._annotations return self._annotations
def annotate(self, annotation): def annotate(self, annotation):
"""Annotates this expression with the given annotation """Annotates this expression with the given annotation.
:param annotation: :param annotation:
""" """
if isinstance(annotation, list): if isinstance(annotation, list):
self._annotations += annotation self._annotations += annotation
@ -43,9 +44,9 @@ class Expression:
def simplify(expression: Expression): def simplify(expression: Expression):
"""Simplify the expression . """Simplify the expression .
:param expression: :param expression:
:return: :return:
""" """
expression.simplify() expression.simplify()
return expression return expression

@ -8,20 +8,19 @@ class Solver:
"""An SMT solver object.""" """An SMT solver object."""
def __init__(self): def __init__(self):
""" """"""
"""
self.raw = z3.Solver() self.raw = z3.Solver()
def set_timeout(self, timeout: int) -> None: def set_timeout(self, timeout: int) -> None:
""" Sets the timeout that will be used by this solver, timeout is in milliseconds """Sets the timeout that will be used by this solver, timeout is in
milliseconds.
:param timeout: :param timeout:
""" """
self.raw.set(timeout=timeout) self.raw.set(timeout=timeout)
def add(self, constraints: list) -> None: def add(self, constraints: list) -> None:
""" Adds the constraints to this solver """Adds the constraints to this solver.
:param constraints: :param constraints:
:return: :return:
@ -33,7 +32,7 @@ class Solver:
self.raw.add(constraints) self.raw.add(constraints)
def append(self, constraints: list) -> None: def append(self, constraints: list) -> None:
""" Adds the constraints to this solver """Adds the constraints to this solver.
:param constraints: :param constraints:
:return: :return:
@ -45,14 +44,15 @@ class Solver:
self.raw.add(constraints) self.raw.add(constraints)
def check(self): def check(self):
""" Returns z3 smt check result """Returns z3 smt check result.
:return: :return:
""" """
return self.raw.check() return self.raw.check()
def model(self): def model(self):
""" Returns z3 model for a solution """Returns z3 model for a solution.
:return: :return:
""" """
return self.raw.model() return self.raw.model()
@ -62,7 +62,7 @@ class Solver:
self.raw.reset() self.raw.reset()
def pop(self, num) -> None: def pop(self, num) -> None:
""" Pop num constraints from this solver """Pop num constraints from this solver.
:param num: :param num:
""" """
@ -70,9 +70,7 @@ class Solver:
class Optimize(Solver): class Optimize(Solver):
""" """An optimizing smt solver."""
An optimizing smt solver
"""
def __init__(self): def __init__(self):
"""Create a new optimizing solver instance.""" """Create a new optimizing solver instance."""
@ -80,14 +78,14 @@ class Optimize(Solver):
self.raw = z3.Optimize() self.raw = z3.Optimize()
def minimize(self, element: Expression): def minimize(self, element: Expression):
""" In solving this solver will try to minimize the passed expression """In solving this solver will try to minimize the passed expression.
:param element: :param element:
""" """
self.raw.minimize(element.raw) self.raw.minimize(element.raw)
def maximize(self, element: Expression): def maximize(self, element: Expression):
""" In solving this solver will try to maximize the passed expression """In solving this solver will try to maximize the passed expression.
:param element: :param element:
""" """

@ -77,7 +77,6 @@ class Mythril(object):
mythril.dump_statespaces(args) mythril.dump_statespaces(args)
mythril.disassemble(contract) mythril.disassemble(contract)
mythril.get_state_variable_from_storage(args) mythril.get_state_variable_from_storage(args)
""" """
def __init__( def __init__(
@ -133,8 +132,8 @@ class Mythril(object):
return mythril_dir return mythril_dir
def _init_config(self): def _init_config(self):
""" """If no config file exists, create it and add default options.
If no config file exists, create it and add default options.
Default LevelDB path is specified based on OS Default LevelDB path is specified based on OS
dynamic loading is set to infura by default in the file dynamic loading is set to infura by default in the file
Returns: leveldb directory Returns: leveldb directory

@ -1,4 +1,5 @@
"""This module contains representation classes for Solidity files, contracts and source mappings.""" """This module contains representation classes for Solidity files, contracts
and source mappings."""
import mythril.laser.ethereum.util as helper import mythril.laser.ethereum.util as helper
from mythril.ethereum.evmcontract import EVMContract from mythril.ethereum.evmcontract import EVMContract
from mythril.ethereum.util import get_solc_json from mythril.ethereum.util import get_solc_json

@ -1,4 +1,5 @@
"""This module contains the dynamic loader logic to get on-chain storage data and dependencies.""" """This module contains the dynamic loader logic to get on-chain storage data
and dependencies."""
from mythril.disassembler.disassembly import Disassembly from mythril.disassembler.disassembly import Disassembly
import logging import logging
import re import re

@ -75,8 +75,9 @@ except ImportError:
class SQLiteDB(object): class SQLiteDB(object):
""" """Simple context manager for sqlite3 databases.
Simple context manager for sqlite3 databases. Commits everything at exit.
Commits everything at exit.
""" """
def __init__(self, path): def __init__(self, path):
@ -112,9 +113,7 @@ class SQLiteDB(object):
class SignatureDB(object, metaclass=Singleton): class SignatureDB(object, metaclass=Singleton):
""" """"""
"""
def __init__(self, enable_online_lookup: bool = False, path: str = None) -> None: def __init__(self, enable_online_lookup: bool = False, path: str = None) -> None:
""" """
@ -146,8 +145,8 @@ class SignatureDB(object, metaclass=Singleton):
) )
def __getitem__(self, item: str) -> List[str]: def __getitem__(self, item: str) -> List[str]:
""" """Provide dict interface db[sighash]
Provide dict interface db[sighash]
:param item: 4-byte signature string :param item: 4-byte signature string
:return: list of matching text signature strings :return: list of matching text signature strings
""" """
@ -155,8 +154,8 @@ class SignatureDB(object, metaclass=Singleton):
@staticmethod @staticmethod
def _normalize_byte_sig(byte_sig: str) -> str: def _normalize_byte_sig(byte_sig: str) -> str:
""" """Adds a leading 0x to the byte signature if it's not already there.
Adds a leading 0x to the byte signature if it's not already there.
:param byte_sig: 4-byte signature string :param byte_sig: 4-byte signature string
:return: normalized byte signature string :return: normalized byte signature string
""" """
@ -184,10 +183,9 @@ class SignatureDB(object, metaclass=Singleton):
) )
def get(self, byte_sig: str, online_timeout: int = 2) -> List[str]: def get(self, byte_sig: str, online_timeout: int = 2) -> List[str]:
""" """Get a function text signature for a byte signature 1) try local
Get a function text signature for a byte signature cache 2) try online lookup (if enabled; if not flagged as unavailable)
1) try local cache
2) try online lookup (if enabled; if not flagged as unavailable)
:param byte_sig: function signature hash as hexstr :param byte_sig: function signature hash as hexstr
:param online_timeout: online lookup timeout :param online_timeout: online lookup timeout
:return: list of matching function text signatures :return: list of matching function text signatures
@ -235,8 +233,8 @@ class SignatureDB(object, metaclass=Singleton):
def import_solidity_file( def import_solidity_file(
self, file_path: str, solc_binary: str = "solc", solc_args: str = None self, file_path: str, solc_binary: str = "solc", solc_args: str = None
): ):
""" """Import Function Signatures from solidity source files.
Import Function Signatures from solidity source files
:param solc_binary: :param solc_binary:
:param solc_args: :param solc_args:
:param file_path: solidity source code file path :param file_path: solidity source code file path
@ -283,8 +281,7 @@ class SignatureDB(object, metaclass=Singleton):
@staticmethod @staticmethod
def lookup_online(byte_sig: str, timeout: int, proxies=None) -> List[str]: def lookup_online(byte_sig: str, timeout: int, proxies=None) -> List[str]:
""" """Lookup function signatures from 4byte.directory.
Lookup function signatures from 4byte.directory.
:param byte_sig: function signature hash as hexstr :param byte_sig: function signature hash as hexstr
:param timeout: optional timeout for online lookup :param timeout: optional timeout for online lookup

@ -1,4 +1,5 @@
"""This module contains functionality used to easily analyse Truffle projects.""" """This module contains functionality used to easily analyse Truffle
projects."""
import os import os
from pathlib import PurePath from pathlib import PurePath
import re import re

@ -1,6 +1,7 @@
"""This file contains the current Mythril version. """This file contains the current Mythril version.
This file is suitable for sourcing inside POSIX shell, e.g. bash as well as for importing into Python. This file is suitable for sourcing inside POSIX shell, e.g. bash as well
as for importing into Python.
""" """
VERSION = "v0.19.11" # NOQA VERSION = "v0.19.11" # NOQA

@ -1,13 +1,11 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """install mythril and deploy source-dist and wheel to pypi.python.org.
install mythril and deploy source-dist and wheel to pypi.python.org
deps (requires up2date version): deps (requires up2date version):
*) pip install --upgrade pip wheel setuptools twine *) pip install --upgrade pip wheel setuptools twine
publish to pypi w/o having to convert Readme.md to RST: publish to pypi w/o having to convert Readme.md to RST:
1) #> python setup.py sdist bdist_wheel 1) #> python setup.py sdist bdist_wheel
2) #> twine upload dist/* #<specify bdist_wheel version to upload>; #optional --repository <testpypi> or --repository-url <testpypi-url> 2) #> twine upload dist/* #<specify bdist_wheel version to upload>; #optional --repository <testpypi> or --repository-url <testpypi-url>
""" """
from setuptools import setup, find_packages from setuptools import setup, find_packages
from setuptools.command.install import install from setuptools.command.install import install
@ -27,14 +25,12 @@ exec(open(str(version_path), "r").read())
class VerifyVersionCommand(install): class VerifyVersionCommand(install):
"""Custom command to verify that the git tag matches our version""" """Custom command to verify that the git tag matches our version."""
description = "verify that the git tag matches our version" description = "verify that the git tag matches our version"
def run(self): def run(self):
""" """"""
"""
tag = os.getenv("CIRCLE_TAG") tag = os.getenv("CIRCLE_TAG")
if tag != VERSION: if tag != VERSION:
@ -45,8 +41,8 @@ class VerifyVersionCommand(install):
def read_file(fname): def read_file(fname):
""" """return file contents.
return file contents
:param fname: path relative to setup.py :param fname: path relative to setup.py
:return: file contents :return: file contents
""" """

@ -17,9 +17,7 @@ MYTHRIL_DIR = TESTS_DIR / "mythril_dir"
class BaseTestCase(TestCase): class BaseTestCase(TestCase):
def setUp(self): def setUp(self):
""" """"""
"""
self.changed_files = [] self.changed_files = []
def compare_files_error_message(self): def compare_files_error_message(self):
@ -46,9 +44,7 @@ class BaseTestCase(TestCase):
self.changed_files.append((input_file, output_expected, output_current)) self.changed_files.append((input_file, output_expected, output_current))
def assert_and_show_changed_files(self): def assert_and_show_changed_files(self):
""" """"""
"""
self.assertEqual( self.assertEqual(
0, len(self.changed_files), msg=self.compare_files_error_message() 0, len(self.changed_files), msg=self.compare_files_error_message()
) )

@ -4,9 +4,7 @@ from tests import BaseTestCase
class EVMContractTestCase(BaseTestCase): class EVMContractTestCase(BaseTestCase):
def setUp(self): def setUp(self):
""" """"""
"""
super().setUp() super().setUp()
self.code = "0x60606040525b603c5b60006010603e565b9050593681016040523660008237602060003683856040603f5a0204f41560545760206000f35bfe5b50565b005b73c3b2ae46792547a96b9f84405e36d0e07edcd05c5b905600a165627a7a7230582062a884f947232ada573f95940cce9c8bfb7e4e14e21df5af4e884941afb55e590029" self.code = "0x60606040525b603c5b60006010603e565b9050593681016040523660008237602060003683856040603f5a0204f41560545760206000f35bfe5b50565b005b73c3b2ae46792547a96b9f84405e36d0e07edcd05c5b905600a165627a7a7230582062a884f947232ada573f95940cce9c8bfb7e4e14e21df5af4e884941afb55e590029"
self.creation_code = "0x60606040525b603c5b60006010603e565b9050593681016040523660008237602060003683856040603f5a0204f41560545760206000f35bfe5b50565b005b73c3b2ae46792547a96b9f84405e36d0e07edcd05c5b905600a165627a7a7230582062a884f947232ada573f95940cce9c8bfb7e4e14e21df5af4e884941afb55e590029" self.creation_code = "0x60606040525b603c5b60006010603e565b9050593681016040523660008237602060003683856040603f5a0204f41560545760206000f35bfe5b50565b005b73c3b2ae46792547a96b9f84405e36d0e07edcd05c5b905600a165627a7a7230582062a884f947232ada573f95940cce9c8bfb7e4e14e21df5af4e884941afb55e590029"
@ -14,9 +12,7 @@ class EVMContractTestCase(BaseTestCase):
class Getinstruction_listTestCase(EVMContractTestCase): class Getinstruction_listTestCase(EVMContractTestCase):
def runTest(self): def runTest(self):
""" """"""
"""
contract = EVMContract(self.code, self.creation_code) contract = EVMContract(self.code, self.creation_code)
disassembly = contract.disassembly disassembly = contract.disassembly
@ -30,9 +26,7 @@ class Getinstruction_listTestCase(EVMContractTestCase):
class GetEASMTestCase(EVMContractTestCase): class GetEASMTestCase(EVMContractTestCase):
def runTest(self): def runTest(self):
""" """"""
"""
contract = EVMContract(self.code) contract = EVMContract(self.code)
instruction_list = contract.get_easm() instruction_list = contract.get_easm()
@ -45,9 +39,7 @@ class GetEASMTestCase(EVMContractTestCase):
class MatchesExpressionTestCase(EVMContractTestCase): class MatchesExpressionTestCase(EVMContractTestCase):
def runTest(self): def runTest(self):
""" """"""
"""
contract = EVMContract(self.code) contract = EVMContract(self.code)
self.assertTrue( self.assertTrue(

@ -87,9 +87,7 @@ def _test_natives(laser_info, test_list, test_name):
class NativeTests(BaseTestCase): class NativeTests(BaseTestCase):
@staticmethod @staticmethod
def runTest(): def runTest():
""" """"""
"""
disassembly = SolidityContract( disassembly = SolidityContract(
"./tests/native_tests.sol", solc_binary=Mythril._init_solc_binary("0.5.0") "./tests/native_tests.sol", solc_binary=Mythril._init_solc_binary("0.5.0")
).disassembly ).disassembly

@ -41,7 +41,7 @@ def _generate_report(input_file):
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def reports(): def reports():
""" Fixture that analyses all reports""" """Fixture that analyses all reports."""
reset_callback_modules() reset_callback_modules()
pool = Pool(cpu_count()) pool = Pool(cpu_count())
input_files = sorted( input_files = sorted(
@ -53,7 +53,8 @@ def reports():
def _assert_empty(changed_files, postfix): def _assert_empty(changed_files, postfix):
""" Asserts there are no changed files and otherwise builds error message""" """Asserts there are no changed files and otherwise builds error
message."""
message = "" message = ""
for input_file in changed_files: for input_file in changed_files:
output_expected = ( output_expected = (
@ -76,7 +77,8 @@ def _assert_empty(changed_files, postfix):
def _assert_empty_json(changed_files): def _assert_empty_json(changed_files):
""" Asserts there are no changed files and otherwise builds error message""" """Asserts there are no changed files and otherwise builds error
message."""
postfix = ".json" postfix = ".json"
expected = [] expected = []
actual = [] actual = []
@ -111,8 +113,8 @@ def _assert_empty_json(changed_files):
def _get_changed_files(postfix, report_builder, reports): def _get_changed_files(postfix, report_builder, reports):
""" """Returns a generator for all unexpected changes in generated reports.
Returns a generator for all unexpected changes in generated reports
:param postfix: The applicable postfix :param postfix: The applicable postfix
:param report_builder: serialization function :param report_builder: serialization function
:param reports: The reports to serialize :param reports: The reports to serialize

@ -8,15 +8,11 @@ class RpcTest(BaseTestCase):
client = None client = None
def setUp(self): def setUp(self):
""" """"""
"""
self.client = EthJsonRpc() self.client = EthJsonRpc()
def tearDown(self): def tearDown(self):
""" """"""
"""
self.client.close() self.client.close()
def test_eth_coinbase(self): def test_eth_coinbase(self):

Loading…
Cancel
Save