"""This module contains the class representing EVM contracts, aka Smart
Contracts."""
import re
import logging
import persistent
from mythril.support.support_utils import sha3
from mythril.disassembler.disassembly import Disassembly
from mythril.support.support_utils import get_code_hash
log = logging.getLogger(__name__)
[docs]
class EVMContract(persistent.Persistent):
"""This class represents an address with associated code (Smart
Contract)."""
def __init__(
self, code="", creation_code="", name="Unknown", enable_online_lookup=False
):
"""Create a new contract.
Workaround: We currently do not support compile-time linking.
Dynamic contract addresses of the format __[contract-name]_____________ are replaced with a generic address
Apply this for creation_code & code
:param code:
:param creation_code:
:param name:
:param enable_online_lookup:
"""
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
self.code = code
self.disassembly = Disassembly(code, enable_online_lookup=enable_online_lookup)
self.creation_disassembly = Disassembly(
creation_code, enable_online_lookup=enable_online_lookup
)
@property
def bytecode_hash(self):
"""
:return: runtime bytecode hash
"""
return get_code_hash(self.code)
@property
def creation_bytecode_hash(self):
"""
:return: Creation bytecode hash
"""
return get_code_hash(self.creation_code)
[docs]
def as_dict(self):
"""
:return:
"""
return {
"name": self.name,
"code": self.code,
"creation_code": self.creation_code,
"disassembly": self.disassembly,
}
[docs]
def get_easm(self):
"""
:return:
"""
return self.disassembly.get_easm()
[docs]
def get_creation_easm(self):
"""
:return:
"""
return self.creation_disassembly.get_easm()
[docs]
def matches_expression(self, expression):
"""
:param expression:
:return:
"""
str_eval = ""
easm_code = None
tokens = re.split("\s+(and|or|not)\s+", expression, re.IGNORECASE)
for token in tokens:
if token in ("and", "or", "not"):
str_eval += " " + token + " "
continue
m = re.match(r"^code#([a-zA-Z0-9\s,\[\]]+)#", token)
if m:
if easm_code is None:
easm_code = self.get_easm()
code = m.group(1).replace(",", "\\n")
str_eval += '"' + code + '" in easm_code'
continue
m = re.match(r"^func#([a-zA-Z0-9\s_,(\\)\[\]]+)#$", token)
if m:
sign_hash = "0x" + sha3(m.group(1))[:4].hex()
str_eval += '"' + sign_hash + '" in self.disassembly.func_hashes'
return eval(str_eval.strip())