diff --git a/Dockerfile b/Dockerfile
index 59c9123d..e204e2d1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -18,15 +18,18 @@ RUN apt-get update \
git \
&& ln -s /usr/bin/python3 /usr/local/bin/python
-COPY . /opt/mythril
+COPY ./requirements.txt /opt/mythril/requirements.txt
RUN cd /opt/mythril \
- && pip3 install -r requirements.txt \
- && python setup.py install
+ && pip3 install -r requirements.txt
RUN locale-gen en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.en
ENV LC_ALL en_US.UTF-8
+COPY . /opt/mythril
+RUN cd /opt/mythril \
+ && python setup.py install
+
ENTRYPOINT ["/usr/local/bin/myth"]
diff --git a/mythril/analysis/modules/dependence_on_predictable_vars.py b/mythril/analysis/modules/dependence_on_predictable_vars.py
index 56cc3156..1e1085ab 100644
--- a/mythril/analysis/modules/dependence_on_predictable_vars.py
+++ b/mythril/analysis/modules/dependence_on_predictable_vars.py
@@ -120,8 +120,8 @@ def solve(call):
model = solver.get_model(call.node.constraints)
logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] MODEL: " + str(model))
- for d in model.decls():
- logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] main model: %s = 0x%x" % (d.name(), model[d].as_long()))
+ for decl in model.decls():
+ logging.debug("[DEPENDENCE_ON_PREDICTABLE_VARS] main model: %s = 0x%x" % (decl.name(), model[decl].as_long()))
return True
except UnsatError:
diff --git a/mythril/analysis/modules/deprecated_ops.py b/mythril/analysis/modules/deprecated_ops.py
index 84f39b6e..2b187e2d 100644
--- a/mythril/analysis/modules/deprecated_ops.py
+++ b/mythril/analysis/modules/deprecated_ops.py
@@ -24,7 +24,7 @@ def execute(statespace):
instruction = state.get_current_instruction()
if instruction['opcode'] == "ORIGIN":
- description = "Function %s retrieves the transaction origin (tx.origin) using the ORIGIN opcode. " \
+ description = "The function `{}` retrieves the transaction origin (tx.origin) using the ORIGIN opcode. " \
"Use msg.sender instead.\nSee also: " \
"https://solidity.readthedocs.io/en/develop/security-considerations.html#tx-origin".format(node.function_name)
diff --git a/mythril/analysis/modules/ether_send.py b/mythril/analysis/modules/ether_send.py
index bfb0d057..00aac6a5 100644
--- a/mythril/analysis/modules/ether_send.py
+++ b/mythril/analysis/modules/ether_send.py
@@ -111,8 +111,8 @@ def execute(statespace):
try:
model = solver.get_model(node.constraints)
- for d in model.decls():
- logging.debug("[ETHER_SEND] main model: %s = 0x%x" % (d.name(), model[d].as_long()))
+ for decl in model.decls():
+ logging.debug("[ETHER_SEND] main model: %s = 0x%x" % (decl.name(), model[decl].as_long()))
debug = "SOLVER OUTPUT:\n" + solver.pretty_print_model(model)
diff --git a/mythril/analysis/modules/multiple_sends.py b/mythril/analysis/modules/multiple_sends.py
index 0df17575..baaea896 100644
--- a/mythril/analysis/modules/multiple_sends.py
+++ b/mythril/analysis/modules/multiple_sends.py
@@ -25,8 +25,8 @@ def execute(statespace):
swc_id=MULTIPLE_SENDS, title="Multiple Calls", _type="Informational")
issue.description = \
- "Multiple sends exist in one transaction, try to isolate each external call into its own transaction." \
- " As external calls can fail accidentally or deliberately.\nConsecutive calls: \n"
+ "Multiple sends exist in one transaction. Try to isolate each external call into its own transaction," \
+ " as external calls can fail accidentally or deliberately.\nConsecutive calls: \n"
for finding in findings:
issue.description += \
@@ -38,7 +38,7 @@ def execute(statespace):
def _explore_nodes(call, statespace):
children = _child_nodes(statespace, call.node)
- sending_children = list(filter(lambda call: call.node in children, statespace.calls))
+ sending_children = list(filter(lambda c: c.node in children, statespace.calls))
return sending_children
diff --git a/mythril/analysis/symbolic.py b/mythril/analysis/symbolic.py
index c9431257..1108f4f7 100644
--- a/mythril/analysis/symbolic.py
+++ b/mythril/analysis/symbolic.py
@@ -16,7 +16,6 @@ class SymExecWrapper:
def __init__(self, contract, address, strategy, dynloader=None, max_depth=22,
execution_timeout=None, create_timeout=None, max_transaction_count=3):
- s_strategy = None
if strategy == 'dfs':
s_strategy = DepthFirstSearchStrategy
elif strategy == 'bfs':
diff --git a/mythril/analysis/templates/callgraph.html b/mythril/analysis/templates/callgraph.html
index 5032b2c2..807ecfc6 100644
--- a/mythril/analysis/templates/callgraph.html
+++ b/mythril/analysis/templates/callgraph.html
@@ -1,7 +1,7 @@
- Laser - Call Graph
+ Call Graph
diff --git a/mythril/analysis/traceexplore.py b/mythril/analysis/traceexplore.py
index 62d9ea50..d70fdcbd 100644
--- a/mythril/analysis/traceexplore.py
+++ b/mythril/analysis/traceexplore.py
@@ -13,8 +13,8 @@ colors = [
{'border': '#4753bf', 'background': '#3b46a1', 'highlight': {'border': '#fff', 'background': '#424db3'}},
]
+
def get_serializable_statespace(statespace):
-
nodes = []
edges = []
@@ -40,10 +40,10 @@ def get_serializable_statespace(statespace):
color = color_map[node.get_cfg_dict()['contract_name']]
- def get_state_accounts(state):
+ def get_state_accounts(node_state):
state_accounts = []
- for key in state.accounts:
- account = state.accounts[key].as_dict
+ for key in node_state.accounts:
+ account = node_state.accounts[key].as_dict
account.pop('code', None)
account['balance'] = str(account['balance'])
diff --git a/mythril/ether/ethcontract.py b/mythril/ether/ethcontract.py
index fbd87b69..801e063b 100644
--- a/mythril/ether/ethcontract.py
+++ b/mythril/ether/ethcontract.py
@@ -12,8 +12,8 @@ class ETHContract(persistent.Persistent):
# Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address
# Apply this for creation_code & code
- creation_code = re.sub(r'(_+.*_+)', 'aa' * 20, creation_code)
- code = re.sub(r'(_+.*_+)', 'aa' * 20, code)
+ creation_code = re.sub(r'(_{2}.{38})', 'aa' * 20, creation_code)
+ code = re.sub(r'(_{2}.{38})', 'aa' * 20, code)
self.creation_code = creation_code
self.name = name
diff --git a/mythril/ether/evm.py b/mythril/ether/evm.py
index 0bcc7206..f1cf2fa5 100644
--- a/mythril/ether/evm.py
+++ b/mythril/ether/evm.py
@@ -7,69 +7,52 @@ from io import StringIO
import re
-def trace(code, calldata = ""):
-
- log_handlers = ['eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage']
-
- output = StringIO()
- stream_handler = StreamHandler(output)
-
- for handler in log_handlers:
- log_vm_op = get_logger(handler)
- log_vm_op.setLevel("TRACE")
- log_vm_op.addHandler(stream_handler)
-
- addr = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF01234567')
-
- state = State()
-
- ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr, 0, addr))
-
- message = vm.Message(addr, addr, 0, 21000, calldata)
-
- res, gas, dat = vm.vm_execute(ext, message, util.safe_decode(code))
-
- stream_handler.flush()
-
- ret = output.getvalue()
-
- lines = ret.split("\n")
-
- trace = []
-
- for line in lines:
-
- m = re.search(r'pc=b\'(\d+)\'.*op=([A-Z0-9]+)', line)
-
- if m:
- pc = m.group(1)
- op = m.group(2)
-
- m = re.match(r'.*stack=(\[.*?\])', line)
-
- if m:
-
- stackitems = re.findall(r'b\'(\d+)\'', m.group(1))
-
- stack = "["
-
- if len(stackitems):
-
- for i in range(0, len(stackitems) - 1):
- stack += hex(int(stackitems[i])) + ", "
-
- stack += hex(int(stackitems[-1]))
-
- stack += "]"
-
- else:
- stack = "[]"
-
- if re.match(r'^PUSH.*', op):
- val = re.search(r'pushvalue=(\d+)', line).group(1)
- pushvalue = hex(int(val))
- trace.append({'pc': pc, 'op': op, 'stack': stack, 'pushvalue': pushvalue})
- else:
- trace.append({'pc': pc, 'op': op, 'stack': stack})
-
- return trace
+def trace(code, calldata=""):
+ log_handlers = ['eth.vm.op', 'eth.vm.op.stack', 'eth.vm.op.memory', 'eth.vm.op.storage']
+ output = StringIO()
+ stream_handler = StreamHandler(output)
+
+ for handler in log_handlers:
+ log_vm_op = get_logger(handler)
+ log_vm_op.setLevel("TRACE")
+ log_vm_op.addHandler(stream_handler)
+
+ addr = bytes.fromhex('0123456789ABCDEF0123456789ABCDEF01234567')
+ state = State()
+
+ ext = messages.VMExt(state, transactions.Transaction(0, 0, 21000, addr, 0, addr))
+ message = vm.Message(addr, addr, 0, 21000, calldata)
+ vm.vm_execute(ext, message, util.safe_decode(code))
+ stream_handler.flush()
+ ret = output.getvalue()
+ lines = ret.split("\n")
+
+ state_trace = []
+ for line in lines:
+ m = re.search(r'pc=b\'(\d+)\'.*op=([A-Z0-9]+)', line)
+ if m:
+ pc = m.group(1)
+ op = m.group(2)
+ m = re.match(r'.*stack=(\[.*?\])', line)
+
+ if m:
+ stackitems = re.findall(r'b\'(\d+)\'', m.group(1))
+ stack = "["
+
+ if len(stackitems):
+ for i in range(0, len(stackitems) - 1):
+ stack += hex(int(stackitems[i])) + ", "
+ stack += hex(int(stackitems[-1]))
+
+ stack += "]"
+ else:
+ stack = "[]"
+
+ if re.match(r'^PUSH.*', op):
+ val = re.search(r'pushvalue=(\d+)', line).group(1)
+ pushvalue = hex(int(val))
+ state_trace.append({'pc': pc, 'op': op, 'stack': stack, 'pushvalue': pushvalue})
+ else:
+ state_trace.append({'pc': pc, 'op': op, 'stack': stack})
+
+ return state_trace
diff --git a/mythril/ethereum/interface/leveldb/accountindexing.py b/mythril/ethereum/interface/leveldb/accountindexing.py
index 7578afd1..9d94c31e 100644
--- a/mythril/ethereum/interface/leveldb/accountindexing.py
+++ b/mythril/ethereum/interface/leveldb/accountindexing.py
@@ -63,16 +63,15 @@ class AccountIndexer(object):
def get_contract_by_hash(self, contract_hash):
"""
- get mapped address by its hash, if not found try indexing
+ get mapped contract_address by its hash, if not found try indexing
"""
- address = self.db.reader._get_address_by_hash(contract_hash)
- if address is not None:
- return address
+ contract_address = self.db.reader._get_address_by_hash(contract_hash)
+ if contract_address is not None:
+ return contract_address
+
else:
raise AddressNotFoundError
- return self.db.reader._get_address_by_hash(contract_hash)
-
def _process(self, startblock):
"""
Processesing method
@@ -82,9 +81,9 @@ class AccountIndexer(object):
addresses = []
for blockNum in range(startblock, startblock + BATCH_SIZE):
- hash = self.db.reader._get_block_hash(blockNum)
- if hash is not None:
- receipts = self.db.reader._get_block_receipts(hash, blockNum)
+ block_hash = self.db.reader._get_block_hash(blockNum)
+ if block_hash is not None:
+ receipts = self.db.reader._get_block_receipts(block_hash, blockNum)
for receipt in receipts:
if receipt.contractAddress is not None and not all(b == 0 for b in receipt.contractAddress):
diff --git a/mythril/ethereum/interface/leveldb/client.py b/mythril/ethereum/interface/leveldb/client.py
index a1b4323b..bdc4b46b 100644
--- a/mythril/ethereum/interface/leveldb/client.py
+++ b/mythril/ethereum/interface/leveldb/client.py
@@ -79,52 +79,43 @@ class LevelDBReader(object):
gets head block header
"""
if not self.head_block_header:
- hash = self.db.get(head_header_key)
- num = self._get_block_number(hash)
- self.head_block_header = self._get_block_header(hash, num)
+ block_hash = self.db.get(head_header_key)
+ num = self._get_block_number(block_hash)
+ self.head_block_header = self._get_block_header(block_hash, num)
# find header with valid state
while not self.db.get(self.head_block_header.state_root) and self.head_block_header.prevhash is not None:
- hash = self.head_block_header.prevhash
- num = self._get_block_number(hash)
- self.head_block_header = self._get_block_header(hash, num)
+ block_hash = self.head_block_header.prevhash
+ num = self._get_block_number(block_hash)
+ self.head_block_header = self._get_block_header(block_hash, num)
return self.head_block_header
- def _get_block_number(self, hash):
- """
- gets block number by hash
- """
- number_key = block_hash_prefix + hash
+ def _get_block_number(self, block_hash):
+ """Get block number by its hash"""
+ number_key = block_hash_prefix + block_hash
return self.db.get(number_key)
- def _get_block_header(self, hash, num):
- """
- get block header by block header hash & number
- """
- header_key = header_prefix + num + hash
+ def _get_block_header(self, block_hash, num):
+ """Get block header by block header hash & number"""
+ header_key = header_prefix + num + block_hash
+
block_header_data = self.db.get(header_key)
header = rlp.decode(block_header_data, sedes=BlockHeader)
return header
- def _get_address_by_hash(self, hash):
- """
- get mapped address by its hash
- """
- address_key = address_prefix + hash
+ def _get_address_by_hash(self, block_hash):
+ """Get mapped address by its hash"""
+ address_key = address_prefix + block_hash
return self.db.get(address_key)
def _get_last_indexed_number(self):
- """
- latest indexed block number
- """
+ """Get latest indexed block number"""
return self.db.get(address_mapping_head_key)
- def _get_block_receipts(self, hash, num):
- """
- get block transaction receipts by block header hash & number
- """
+ def _get_block_receipts(self, block_hash, num):
+ """Get block transaction receipts by block header hash & number"""
number = _format_block_number(num)
- receipts_key = block_receipts_prefix + number + hash
+ receipts_key = block_receipts_prefix + number + block_hash
receipts_data = self.db.get(receipts_key)
receipts = rlp.decode(receipts_data, sedes=CountableList(ReceiptForStorage))
return receipts
@@ -216,12 +207,10 @@ class EthLevelDB(object):
if not cnt % 1000:
logging.info("Searched %d contracts" % cnt)
- def contract_hash_to_address(self, hash):
- """
- tries to find corresponding account address
- """
+ def contract_hash_to_address(self, contract_hash):
+ """Tries to find corresponding account address"""
- address_hash = binascii.a2b_hex(utils.remove_0x_head(hash))
+ address_hash = binascii.a2b_hex(utils.remove_0x_head(contract_hash))
indexer = AccountIndexer(self)
return _encode_hex(indexer.get_contract_by_hash(address_hash))
@@ -230,9 +219,9 @@ class EthLevelDB(object):
"""
gets block header by block number
"""
- hash = self.reader._get_block_hash(number)
+ block_hash = self.reader._get_block_hash(number)
block_number = _format_block_number(number)
- return self.reader._get_block_header(hash, block_number)
+ return self.reader._get_block_header(block_hash, block_number)
def eth_getBlockByNumber(self, number):
"""
diff --git a/mythril/ethereum/interface/leveldb/state.py b/mythril/ethereum/interface/leveldb/state.py
index e8f86331..83507b69 100644
--- a/mythril/ethereum/interface/leveldb/state.py
+++ b/mythril/ethereum/interface/leveldb/state.py
@@ -43,9 +43,9 @@ class Account(rlp.Serializable):
('code_hash', hash32)
]
- def __init__(self, nonce, balance, storage, code_hash, db, address):
+ def __init__(self, nonce, balance, storage, code_hash, db, addr):
self.db = db
- self.address = address
+ self.address = addr
super(Account, self).__init__(nonce, balance, storage, code_hash)
self.storage_cache = {}
self.storage_trie = SecureTrie(Trie(self.db))
@@ -73,12 +73,12 @@ class Account(rlp.Serializable):
return self.storage_cache[key]
@classmethod
- def blank_account(cls, db, address, initial_nonce=0):
+ def blank_account(cls, db, addr, initial_nonce=0):
"""
creates a blank account
"""
db.put(BLANK_HASH, b'')
- o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, db, address)
+ o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, db, addr)
o.existent_at_start = False
return o
@@ -100,21 +100,21 @@ class State:
self.journal = []
self.cache = {}
- def get_and_cache_account(self, address):
- """
- gets and caches an account for an addres, creates blank if not found
- """
- if address in self.cache:
- return self.cache[address]
- rlpdata = self.secure_trie.get(address)
- if rlpdata == trie.BLANK_NODE and len(address) == 32: # support for hashed addresses
- rlpdata = self.trie.get(address)
+ def get_and_cache_account(self, addr):
+ """Gets and caches an account for an addres, creates blank if not found"""
+
+ if addr in self.cache:
+ return self.cache[addr]
+ rlpdata = self.secure_trie.get(addr)
+ if rlpdata == trie.BLANK_NODE and len(addr) == 32: # support for hashed addresses
+ rlpdata = self.trie.get(addr)
+
if rlpdata != trie.BLANK_NODE:
- o = rlp.decode(rlpdata, Account, db=self.db, address=address)
+ o = rlp.decode(rlpdata, Account, db=self.db, address=addr)
else:
o = Account.blank_account(
- self.db, address, 0)
- self.cache[address] = o
+ self.db, addr, 0)
+ self.cache[addr] = o
o._mutable = True
o._cached_rlp = None
return o
diff --git a/mythril/interfaces/cli.py b/mythril/interfaces/cli.py
index 1784add4..0562cb90 100644
--- a/mythril/interfaces/cli.py
+++ b/mythril/interfaces/cli.py
@@ -17,8 +17,8 @@ from mythril.mythril import Mythril
from mythril.version import VERSION
-def exit_with_error(format, message):
- if format == 'text' or format == 'markdown':
+def exit_with_error(format_, message):
+ if format_ == 'text' or format_ == 'markdown':
print(message)
else:
result = {'success': False, 'error': str(message), 'issues': []}
diff --git a/mythril/laser/ethereum/call.py b/mythril/laser/ethereum/call.py
index 583c6b2e..d599d2be 100644
--- a/mythril/laser/ethereum/call.py
+++ b/mythril/laser/ethereum/call.py
@@ -99,7 +99,7 @@ def get_callee_account(global_state, callee_address, dynamic_loader):
try:
code = dynamic_loader.dynld(environment.active_account.address, callee_address)
- except Exception as e:
+ except Exception:
logging.debug("Unable to execute dynamic loader.")
raise ValueError()
if code is None:
diff --git a/mythril/laser/ethereum/instructions.py b/mythril/laser/ethereum/instructions.py
index cea603b4..647b19c9 100644
--- a/mythril/laser/ethereum/instructions.py
+++ b/mythril/laser/ethereum/instructions.py
@@ -497,7 +497,6 @@ class Instruction:
global keccak_function_manager
state = global_state.mstate
- environment = global_state.environment
op0, op1 = state.stack.pop(), state.stack.pop()
try:
@@ -701,12 +700,9 @@ class Instruction:
try:
# Attempt to concretize value
-
_bytes = util.concrete_int_to_bytes(value)
-
- state.memory[mstart:mstart+len(_bytes)] = _bytes
-
- except (AttributeError, TypeError):
+ state.memory[mstart: mstart + len(_bytes)] = _bytes
+ except:
try:
state.memory[mstart] = value
except TypeError:
@@ -948,7 +944,7 @@ class Instruction:
state = global_state.mstate
dpth = int(self.op_code[3:])
state.stack.pop(), state.stack.pop()
- [state.stack.pop() for x in range(dpth)]
+ [state.stack.pop() for _ in range(dpth)]
# Not supported
return [global_state]
diff --git a/mythril/laser/ethereum/state.py b/mythril/laser/ethereum/state.py
index 16c01c3a..fd35c86c 100644
--- a/mythril/laser/ethereum/state.py
+++ b/mythril/laser/ethereum/state.py
@@ -278,10 +278,10 @@ class GlobalState:
def new_bitvec(self, name, size=256):
transaction_id = self.current_transaction.id
- node_id = self.node.uid
return BitVec("{}_{}".format(transaction_id, name), size)
+
class WorldState:
"""
The WorldState class represents the world state as described in the yellow paper
diff --git a/mythril/laser/ethereum/transaction/transaction_models.py b/mythril/laser/ethereum/transaction/transaction_models.py
index 35826bcd..4060c4e1 100644
--- a/mythril/laser/ethereum/transaction/transaction_models.py
+++ b/mythril/laser/ethereum/transaction/transaction_models.py
@@ -129,7 +129,7 @@ class ContractCreationTransaction:
def end(self, global_state, return_data=None, revert=False):
- if not all([isinstance(element, int) for element in return_data]):
+ if not all([isinstance(element, int) for element in return_data]) or len(return_data) == 0:
self.return_data = None
raise TransactionEndSignal(global_state)
@@ -137,6 +137,7 @@ class ContractCreationTransaction:
global_state.environment.active_account.code = Disassembly(contract_code)
self.return_data = global_state.environment.active_account.address
+ assert global_state.environment.active_account.code.instruction_list != []
raise TransactionEndSignal(global_state, revert=revert)
diff --git a/mythril/mythril.py b/mythril/mythril.py
index 123ab904..db80ef09 100644
--- a/mythril/mythril.py
+++ b/mythril/mythril.py
@@ -87,7 +87,7 @@ class Mythril(object):
self.sigs = signatures.SignatureDb()
try:
self.sigs.open() # tries mythril_dir/signatures.json by default (provide path= arg to make this configurable)
- except FileNotFoundError as fnfe:
+ except FileNotFoundError:
logging.info(
"No signature database found. Creating database if sigs are loaded in: " + self.sigs.signatures_file + "\n" +
"Consider replacing it with the pre-initialized database at https://raw.githubusercontent.com/ConsenSys/mythril/master/signatures.json")
@@ -261,8 +261,7 @@ class Mythril(object):
def search_db(self, search):
- def search_callback(contract, address, balance):
-
+ def search_callback(_, address, balance):
print("Address: " + address + ", balance: " + str(balance))
try:
@@ -290,10 +289,10 @@ class Mythril(object):
code = self.eth.eth_getCode(address)
except FileNotFoundError as e:
raise CriticalError("IPC error: " + str(e))
- except ConnectionError as e:
+ except ConnectionError:
raise CriticalError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.")
except Exception as e:
- raise CriticalError("IPC / RPC error: " + str(e))
+ raise CriticalError("IPC / RPC error: " + str(e))
else:
if code == "0x" or code == "0x0":
raise CriticalError("Received an empty response from eth_getCode. Check the contract address and verify that you are on the correct chain.")
@@ -435,7 +434,7 @@ class Mythril(object):
outtxt.append("{}: {}".format(hex(i), self.eth.eth_getStorageAt(address, i)))
except FileNotFoundError as e:
raise CriticalError("IPC error: " + str(e))
- except ConnectionError as e:
+ except ConnectionError:
raise CriticalError("Could not connect to RPC server. Make sure that your node is running and that RPC parameters are set correctly.")
return '\n'.join(outtxt)
diff --git a/static/Ownable.html b/static/Ownable.html
index 964558e4..9dff1f1a 100644
--- a/static/Ownable.html
+++ b/static/Ownable.html
@@ -1,5 +1,7 @@
+
-
+
+ Call Graph