Source code for mythril.laser.ethereum.state.account

"""This module contains account-related functionality.

This includes classes representing accounts and their storage.
"""
import logging
from copy import copy, deepcopy
from typing import Any, Dict, Union, Set


from mythril.laser.smt import Array, K, BitVec, simplify, BaseArray, If, Bool
from mythril.disassembler.disassembly import Disassembly
from mythril.laser.smt import symbol_factory
from mythril.support.support_args import args

log = logging.getLogger(__name__)


[docs]class Storage: """Storage class represents the storage of an Account.""" def __init__(self, concrete=False, address=None, dynamic_loader=None) -> None: """Constructor for Storage. :param concrete: bool indicating whether to interpret uninitialized storage as concrete versus symbolic """ if concrete and args.unconstrained_storage is False: self._standard_storage: BaseArray = K(256, 256, 0) else: self._standard_storage = Array(f"Storage{address}", 256, 256) self.printable_storage: Dict[BitVec, BitVec] = {} self.dynld = dynamic_loader self.storage_keys_loaded: Set[int] = set() self.address = address # Stores all keys set in the storage self.keys_set: Set[BitVec] = set() # Stores all get keys in the storage self.keys_get: Set[BitVec] = set() def __getitem__(self, item: BitVec) -> BitVec: storage = self._standard_storage self.keys_get.add(item) if ( self.address and self.address.value != 0 and item.symbolic is False and int(item.value) not in self.storage_keys_loaded and (self.dynld and self.dynld.active) and args.unconstrained_storage is False ): try: value = symbol_factory.BitVecVal( int( self.dynld.read_storage( contract_address="0x{:040X}".format(self.address.value), index=int(item.value), ), 16, ), 256, ) for key in self.keys_set: value = If(key == item, storage[item], value) storage[item] = value self.storage_keys_loaded.add(int(item.value)) self.printable_storage[item] = storage[item] except ValueError as e: log.debug("Couldn't read storage at %s: %s", item, e) return simplify(storage[item]) def __setitem__(self, key, value: Any) -> None: if isinstance(value, Bool): value = If(value, 1, 0) self.printable_storage[key] = value self._standard_storage[key] = value self.keys_set.add(key) if key.symbolic is False: self.storage_keys_loaded.add(int(key.value)) def __deepcopy__(self, memodict=dict()): concrete = isinstance(self._standard_storage, K) storage = Storage( concrete=concrete, address=self.address, dynamic_loader=self.dynld ) storage._standard_storage = deepcopy(self._standard_storage) storage.printable_storage = copy(self.printable_storage) storage.storage_keys_loaded = copy(self.storage_keys_loaded) storage.keys_set = deepcopy(self.keys_set) storage.keys_get = deepcopy(self.keys_get) return storage def __str__(self) -> str: # TODO: Do something better here return str(self.printable_storage)
[docs]class Account: """Account class representing ethereum accounts.""" def __init__( self, address: Union[BitVec, str], code=None, contract_name=None, balances: Array = None, concrete_storage=False, dynamic_loader=None, nonce=0, ) -> None: """Constructor for account. :param address: Address of the account :param code: The contract code of the account :param contract_name: The name associated with the account :param balances: The balance for the account :param concrete_storage: Interpret storage as concrete """ self.concrete_storage = concrete_storage self.nonce = nonce self.code = code or Disassembly("") self.address = ( address if isinstance(address, BitVec) else symbol_factory.BitVecVal(int(address, 16), 256) ) self.storage = Storage( concrete_storage, address=self.address, dynamic_loader=dynamic_loader ) # Metadata if contract_name is None: self.contract_name = ( "{0:#0{1}x}".format(self.address.value, 42) if not self.address.symbolic else "unknown" ) else: self.contract_name = contract_name self.deleted = False self._balances = balances self.balance = lambda: self._balances[self.address] def __str__(self) -> str: return str(self.as_dict)
[docs] def set_balance(self, balance: Union[int, BitVec]) -> None: """ :param balance: """ balance = ( symbol_factory.BitVecVal(balance, 256) if isinstance(balance, int) else balance ) assert self._balances is not None self._balances[self.address] = balance
[docs] def set_storage(self, storage: Dict): """ Sets concrete storage """ for key, value in storage.items(): concrete_key, concrete_value = int(key, 16), int(value, 16) self.storage[ symbol_factory.BitVecVal(concrete_key, 256) ] = symbol_factory.BitVecVal(concrete_value, 256)
[docs] def add_balance(self, balance: Union[int, BitVec]) -> None: """ :param balance: """ balance = ( symbol_factory.BitVecVal(balance, 256) if isinstance(balance, int) else balance ) self._balances[self.address] += balance
@property def as_dict(self) -> Dict: """ :return: """ return { "nonce": self.nonce, "code": self.serialised_code(), "balance": self.balance(), "storage": self.storage, }
[docs] def serialised_code(self): if type(self.code.bytecode) == str: return self.code.bytecode new_code = "0x" for byte in self.code.bytecode: if type(byte) == int: new_code += hex(byte) else: new_code += "<call_data>" return new_code
def __copy__(self, memodict={}): new_account = Account( address=self.address, code=self.code, contract_name=self.contract_name, balances=deepcopy(self._balances), concrete_storage=self.concrete_storage, nonce=self.nonce, ) new_account.storage = deepcopy(self.storage) new_account.code = self.code return new_account