Example #1
0
    def commit_state(self):
        """Commit account caches"""
        """Write the acount caches on the corresponding tries."""
        changes = []
        if len(self.journal) == 0:
            # log_state.trace('delta', changes=[])
            return
        addresses = sorted(list(self.caches['all'].keys()))
        for addr in addresses:
            acct = self._get_acct(addr)

            # storage
            for field in ('balance', 'nonce', 'code', 'storage'):
                if addr in self.caches[field]:
                    v = self.caches[field][addr]
                    changes.append([field, addr, v])
                    setattr(acct, field, v)

            t = SecureTrie(Trie(self.db, acct.storage))
            for k, v in self.caches.get(b'storage:' + addr, {}).items():
                enckey = utils.zpad(utils.coerce_to_bytes(k), 32)
                val = rlp.encode(v)
                changes.append(['storage', addr, k, v])
                if v:
                    t.update(enckey, val)
                else:
                    t.delete(enckey)
            acct.storage = t.root_hash
            self.state.update(addr, rlp.encode(acct))
        log_state.trace('delta', changes=changes)
        self.reset_cache()
        self.db.put(b'validated:' + self.hash, '1')
Example #2
0
    def account_to_dict(self, address, with_storage_root=False,
                        with_storage=True):
        """Serialize an account to a dictionary with human readable entries.

        :param address: the 20 bytes account address
        :param with_storage_root: include the account's storage root
        :param with_storage: include the whole account's storage
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20

        if with_storage_root:
            # if there are uncommited account changes the current storage root
            # is meaningless
            assert len(self.journal) == 0
        med_dict = {}

        account = self._get_acct(address)
        for field in ('balance', 'nonce'):
            value = self.caches[field].get(address, getattr(account, field))
            med_dict[field] = to_string(value)
        code = self.caches['code'].get(address, account.code)
        med_dict['code'] = b'0x' + encode_hex(code)

        storage_trie = SecureTrie(Trie(self.db, account.storage))
        if with_storage_root:
            med_dict['storage_root'] = encode_hex(storage_trie.get_root_hash())
        if with_storage:
            med_dict['storage'] = {}
            d = storage_trie.to_dict()
            subcache = self.caches.get(b'storage:' + address, {})
            subkeys = [utils.zpad(utils.coerce_to_bytes(kk), 32)
                       for kk in list(subcache.keys())]
            for k in list(d.keys()) + subkeys:
                v = d.get(k, None)
                v2 = subcache.get(utils.big_endian_to_int(k), None)
                hexkey = b'0x' + encode_hex(utils.zunpad(k))
                if v2 is not None:
                    if v2 != 0:
                        med_dict['storage'][hexkey] = \
                            b'0x' + encode_hex(utils.int_to_big_endian(v2))
                elif v is not None:
                    med_dict['storage'][hexkey] = b'0x' + encode_hex(rlp.decode(v))

        return med_dict
Example #3
0
class State():
    def __init__(self, root=b'', env=Env(), **kwargs):
        self.env = env
        self.trie = SecureTrie(Trie(self.db, root))
        for k, v in STATE_DEFAULTS.items():
            setattr(self, k, kwargs.get(k, copy.copy(v)))
        self.journal = []
        self.cache = {}
        self.log_listeners = []

    @property
    def db(self):
        return self.env.db

    @property
    def config(self):
        return self.env.config

    def get_block_hash(self, n):
        if self.block_number < n or n > 256 or n < 0:
            o = b'\x00' * 32
        else:
            o = self.prev_headers[n].hash if self.prev_headers[
                n] else b'\x00' * 32
        return o

    def add_block_header(self, block_header):
        self.prev_headers = [block_header] + self.prev_headers

    def get_and_cache_account(self, address):
        if address in self.cache:
            return self.cache[address]
        rlpdata = self.trie.get(address)
        if rlpdata != trie.BLANK_NODE:
            o = rlp.decode(rlpdata, Account, env=self.env)
        else:
            o = Account.blank_account(self.env,
                                      self.config['ACCOUNT_INITIAL_NONCE'])
        self.cache[address] = o
        o._mutable = True
        o._cached_rlp = None
        return o

    def get_balance(self, address):
        return self.get_and_cache_account(
            utils.normalize_address(address)).balance

    def get_code(self, address):
        return self.get_and_cache_account(
            utils.normalize_address(address)).code

    def get_nonce(self, address):
        return self.get_and_cache_account(
            utils.normalize_address(address)).nonce

    def set_and_journal(self, acct, param, val):
        # self.journal.append((acct, param, getattr(acct, param)))
        preval = getattr(acct, param)
        self.journal.append(lambda: setattr(acct, param, preval))
        setattr(acct, param, val)

    def set_balance(self, address, value):
        acct = self.get_and_cache_account(utils.normalize_address(address))
        self.set_and_journal(acct, 'balance', value)
        self.set_and_journal(acct, 'touched', True)

    def set_code(self, address, value):
        # assert is_string(value)
        acct = self.get_and_cache_account(utils.normalize_address(address))
        self.set_and_journal(acct, 'code', value)
        self.set_and_journal(acct, 'touched', True)

    def set_nonce(self, address, value):
        acct = self.get_and_cache_account(utils.normalize_address(address))
        self.set_and_journal(acct, 'nonce', value)
        self.set_and_journal(acct, 'touched', True)

    def delta_balance(self, address, value):
        address = utils.normalize_address(address)
        acct = self.get_and_cache_account(address)
        newbal = acct.balance + value
        self.set_and_journal(acct, 'balance', newbal)
        self.set_and_journal(acct, 'touched', True)

    def increment_nonce(self, address):
        address = utils.normalize_address(address)
        acct = self.get_and_cache_account(address)
        newnonce = acct.nonce + 1
        self.set_and_journal(acct, 'nonce', newnonce)
        self.set_and_journal(acct, 'touched', True)

    def get_storage_data(self, address, key):
        return self.get_and_cache_account(
            utils.normalize_address(address)).get_storage_data(key)

    def set_storage_data(self, address, key, value):
        acct = self.get_and_cache_account(utils.normalize_address(address))
        preval = acct.get_storage_data(key)
        acct.set_storage_data(key, value)
        self.journal.append(lambda: acct.set_storage_data(key, preval))
        self.set_and_journal(acct, 'touched', True)

    def add_suicide(self, address):
        self.suicides.append(address)
        self.journal.append(lambda: self.suicides.pop())

    def add_log(self, log):
        for listener in self.log_listeners:
            listener(log)
        self.logs.append(log)
        self.journal.append(lambda: self.logs.pop())

    def add_receipt(self, receipt):
        self.receipts.append(receipt)
        self.journal.append(lambda: self.receipts.pop())

    def add_refund(self, value):
        preval = self.refunds
        self.refunds += value
        self.journal.append(lambda: setattr(self.refunds, preval))

    def snapshot(self):
        return (self.trie.root_hash, len(self.journal),
                {k: copy.copy(getattr(self, k))
                 for k in STATE_DEFAULTS})

    def revert(self, snapshot):
        h, L, auxvars = snapshot
        while len(self.journal) > L:
            lastitem = self.journal.pop()
            lastitem()
        if h != self.trie.root_hash:
            assert L == 0
            self.trie.root_hash = h
            self.cache = {}
        for k in STATE_DEFAULTS:
            setattr(self, k, copy.copy(auxvars[k]))

    def set_param(self, k, v):
        preval = getattr(self, k)
        self.journal.append(lambda: setattr(self, k, preval))
        setattr(self, k, v)

    def is_SERENITY(self, at_fork_height=False):
        if at_fork_height:
            return self.block_number == self.config['SERENITY_FORK_BLKNUM']
        else:
            return self.block_number >= self.config['SERENITY_FORK_BLKNUM']

    def is_HOMESTEAD(self, at_fork_height=False):
        if at_fork_height:
            return self.block_number == self.config['HOMESTEAD_FORK_BLKNUM']
        else:
            return self.block_number >= self.config['HOMESTEAD_FORK_BLKNUM']

    def is_METROPOLIS(self, at_fork_height=False):
        if at_fork_height:
            return self.block_number == self.config['METROPOLIS_FORK_BLKNUM']
        else:
            return self.block_number >= self.config['METROPOLIS_FORK_BLKNUM']

    def is_ANTI_DOS(self, at_fork_height=False):
        if at_fork_height:
            return self.block_number == self.config['ANTI_DOS_FORK_BLKNUM']
        else:
            return self.block_number >= self.config['ANTI_DOS_FORK_BLKNUM']

    def is_SPURIOUS_DRAGON(self, at_fork_height=False):
        if at_fork_height:
            return self.block_number == self.config[
                'SPURIOUS_DRAGON_FORK_BLKNUM']
        else:
            return self.block_number >= self.config[
                'SPURIOUS_DRAGON_FORK_BLKNUM']

    def is_DAO(self, at_fork_height=False):
        if at_fork_height:
            return self.block_number == self.config['DAO_FORK_BLKNUM']
        else:
            return self.block_number >= self.config['DAO_FORK_BLKNUM']

    def account_exists(self, address):
        if self.is_SPURIOUS_DRAGON():
            o = not self.get_and_cache_account(
                utils.normalize_address(address)).is_blank()
        else:
            a = self.get_and_cache_account(address)
            if a.touched:
                o = not a.deleted
            else:
                o = a.existent_at_start
        return o

    def transfer_value(self, from_addr, to_addr, value):
        assert value >= 0
        if self.get_balance(from_addr) >= value:
            self.delta_balance(from_addr, -value)
            self.delta_balance(to_addr, value)
            return True
        return False

    def account_to_dict(self, address):
        return self.get_and_cache_account(
            utils.normalize_address(address)).to_dict()

    def commit(self, allow_empties=False):
        for addr, acct in self.cache.items():
            if acct.touched:
                acct.commit()
                if acct.exists or allow_empties or (
                        not self.is_SPURIOUS_DRAGON() and not acct.deleted):
                    # print('upd', encode_hex(addr))
                    self.trie.update(addr, rlp.encode(acct))
                else:
                    # print('del', encode_hex(addr))
                    self.trie.delete(addr)
        self.cache = {}
        self.journal = []

    def to_dict(self):
        for addr in self.trie.to_dict().keys():
            self.get_and_cache_account(addr)
        return {
            encode_hex(addr): acct.to_dict()
            for addr, acct in self.cache.items()
        }

    def del_account(self, address):
        self.set_balance(address, 0)
        self.set_nonce(address, 0)
        self.set_code(address, b'')
        self.reset_storage(address)
        self.set_and_journal(
            self.get_and_cache_account(utils.normalize_address(address)),
            'deleted', True)

    def reset_storage(self, address):
        acct = self.get_and_cache_account(address)
        pre_cache = acct.storage_cache
        acct.storage_cache = {}
        self.journal.append(lambda: setattr(acct, 'storage_cache', pre_cache))
        pre_root = acct.storage_trie.root_hash
        self.journal.append(
            lambda: setattr(acct.storage_trie, 'root_hash', pre_root))
        acct.storage_trie.root_hash = BLANK_ROOT

    # Creates a snapshot from a state
    def to_snapshot(self, root_only=False, no_prevblocks=False):
        snapshot = {}
        if root_only:
            # Smaller snapshot format that only includes the state root
            # (requires original DB to re-initialize)
            snapshot["state_root"] = '0x' + encode_hex(self.trie.root_hash)
        else:
            # "Full" snapshot
            snapshot["alloc"] = self.to_dict()
        # Save non-state-root variables
        for k, default in STATE_DEFAULTS.items():
            default = copy.copy(default)
            v = getattr(self, k)
            if is_numeric(default):
                snapshot[k] = str(v)
            elif isinstance(default, (str, bytes)):
                snapshot[k] = '0x' + encode_hex(v)
            elif k == 'prev_headers' and not no_prevblocks:
                snapshot[k] = [
                    prev_header_to_dict(h)
                    for h in v[:self.config['PREV_HEADER_DEPTH']]
                ]
            elif k == 'recent_uncles' and not no_prevblocks:
                snapshot[k] = {
                    str(n): ['0x' + encode_hex(h) for h in headers]
                    for n, headers in v.items()
                }
        return snapshot

    # Creates a state from a snapshot
    @classmethod
    def from_snapshot(cls, snapshot_data, env):
        state = State(env=env)
        if "alloc" in snapshot_data:
            for addr, data in snapshot_data["alloc"].items():
                if len(addr) == 40:
                    addr = decode_hex(addr)
                assert len(addr) == 20
                if 'wei' in data:
                    state.set_balance(addr, parse_as_int(data['wei']))
                if 'balance' in data:
                    state.set_balance(addr, parse_as_int(data['balance']))
                if 'code' in data:
                    state.set_code(addr, parse_as_bin(data['code']))
                if 'nonce' in data:
                    state.set_nonce(addr, parse_as_int(data['nonce']))
                if 'storage' in data:
                    for k, v in data['storage'].items():
                        state.set_storage_data(addr, parse_as_bin(k),
                                               parse_as_bin(v))
        elif "state_root" in snapshot_data:
            state.trie.root_hash = parse_as_bin(snapshot_data["state_root"])
        else:
            raise Exception(
                "Must specify either alloc or state root parameter")
        for k, default in STATE_DEFAULTS.items():
            default = copy.copy(default)
            v = snapshot_data[k] if k in snapshot_data else None
            if is_numeric(default):
                setattr(state, k,
                        parse_as_int(v) if k in snapshot_data else default)
            elif is_string(default):
                setattr(state, k,
                        parse_as_bin(v) if k in snapshot_data else default)
            elif k == 'prev_headers':
                if k in snapshot_data:
                    headers = [dict_to_prev_header(h) for h in v]
                else:
                    headers = default
                setattr(state, k, headers)
            elif k == 'recent_uncles':
                if k in snapshot_data:
                    uncles = {}
                    for height, _uncles in v.items():
                        uncles[int(height)] = []
                        for uncle in _uncles:
                            uncles[int(height)].append(parse_as_bin(uncle))
                else:
                    uncles = default
                setattr(state, k, uncles)
        state.commit()
        return state

    def ephemeral_clone(self):
        snapshot = self.to_snapshot(root_only=True, no_prevblocks=True)
        env2 = Env(OverlayDB(self.env.db), self.env.config)
        s = State.from_snapshot(snapshot, env2)
        for param in STATE_DEFAULTS:
            setattr(s, param, getattr(self, param))
        s.recent_uncles = self.recent_uncles
        s.prev_headers = self.prev_headers
        for acct in self.cache.values():
            assert not acct.touched
        s.journal = copy.copy(self.journal)
        s.cache = {}
        return s
Example #4
0
 def __init__(self, db, root):
     self.db = db
     self.trie = Trie(self.db, root)
     self.secureTrie = SecureTrie(self.trie)
     self.journal = []
     self.cache = {}
Example #5
0
class Account(rlp.Serializable):
    """adjusted account from ethereum.state."""

    fields = [
        ("nonce", big_endian_int),
        ("balance", big_endian_int),
        ("storage", trie_root),
        ("code_hash", hash32),
    ]

    def __init__(self, nonce, balance, storage, code_hash, db, addr):
        """

        :param nonce:
        :param balance:
        :param storage:
        :param code_hash:
        :param db:
        :param addr:
        """
        self.db = db
        self.address = addr
        super(Account, self).__init__(nonce, balance, storage, code_hash)
        self.storage_cache = {}
        self.storage_trie = SecureTrie(Trie(self.db))
        self.storage_trie.root_hash = self.storage
        self.touched = False
        self.existent_at_start = True
        self._mutable = True
        self.deleted = False

    @property
    def code(self):
        """code rlp data."""
        return self.db.get(self.code_hash)

    def get_storage_data(self, key):
        """get storage data.

        :param key:
        :return:
        """
        if key not in self.storage_cache:
            v = self.storage_trie.get(utils.encode_int32(key))
            self.storage_cache[key] = utils.big_endian_to_int(
                rlp.decode(v) if v else b"")
        return self.storage_cache[key]

    @classmethod
    def blank_account(cls, db, addr, initial_nonce=0):
        """creates a blank account.

        :param db:
        :param addr:
        :param initial_nonce:
        :return:
        """
        db.put(BLANK_HASH, b"")
        o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, db, addr)
        o.existent_at_start = False
        return o

    def is_blank(self):
        """checks if is a blank account.

        :return:
        """
        return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH
Example #6
0
class Account(rlp.Serializable):

    fields = [('nonce', big_endian_int), ('balance', big_endian_int),
              ('storage', trie_root), ('code_hash', hash32)]

    def __init__(self, nonce, balance, storage, code_hash, env):
        assert isinstance(env.db, BaseDB)
        self.env = env
        super(Account, self).__init__(nonce, balance, storage, code_hash)
        self.storage_cache = {}
        self.storage_trie = SecureTrie(Trie(self.env.db))
        self.storage_trie.root_hash = self.storage
        self.touched = False
        self.existent_at_start = True
        self._mutable = True
        self.deleted = False

    def commit(self):
        for k, v in self.storage_cache.items():
            if v:
                self.storage_trie.update(utils.encode_int32(k), rlp.encode(v))
            else:
                self.storage_trie.delete(utils.encode_int32(k))
        self.storage_cache = {}
        self.storage = self.storage_trie.root_hash

    @property
    def code(self):
        return self.env.db.get(self.code_hash)

    @code.setter
    def code(self, value):
        self.code_hash = utils.sha3(value)
        # Technically a db storage leak, but doesn't really matter; the only
        # thing that fails to get garbage collected is when code disappears due
        # to a suicide
        self.env.db.put(self.code_hash, value)
        # self.env.db.inc_refcount(self.code_hash, value)

    def get_storage_data(self, key):
        if key not in self.storage_cache:
            v = self.storage_trie.get(utils.encode_int32(key))
            self.storage_cache[key] = utils.big_endian_to_int(
                rlp.decode(v) if v else b'')
        return self.storage_cache[key]

    def set_storage_data(self, key, value):
        self.storage_cache[key] = value

    @classmethod
    def blank_account(cls, env, initial_nonce=0):
        env.db.put(BLANK_HASH, b'')
        o = cls(initial_nonce, 0, trie.BLANK_ROOT, BLANK_HASH, env)
        o.existent_at_start = False
        return o

    def is_blank(self):
        return self.nonce == 0 and self.balance == 0 and self.code_hash == BLANK_HASH

    @property
    def exists(self):
        if self.is_blank():
            return self.existent_at_start and not self.touched
        return True

    def to_dict(self):
        odict = self.storage_trie.to_dict()
        for k, v in self.storage_cache.items():
            odict[utils.encode_int(k)] = rlp.encode(utils.encode_int(v))
        return {
            'balance': str(self.balance),
            'nonce': str(self.nonce),
            'code': '0x' + encode_hex(self.code),
            'storage': {
                '0x' + encode_hex(key.lstrip(b'\x00') or b'\x00'):
                '0x' + encode_hex(rlp.decode(val))
                for key, val in odict.items()
            }
        }
Example #7
0
 def state_root(self, value):
     self.state = SecureTrie(Trie(self.db, value))
     self.reset_cache()
Example #8
0
    def __init__(self, header, transaction_list=[], uncles=[], db=None,
                 parent=None, making=False):
        if db is None:
            raise TypeError("No database object given")
        self.db = db
        self.header = header
        self.uncles = uncles

        self.uncles = uncles
        self.suicides = []
        self.logs = []
        self.log_listeners = []
        self.refunds = 0

        self.ether_delta = 0

        # Journaling cache for state tree updates
        self.caches = {
            'balance': {},
            'nonce': {},
            'code': {},
            'storage': {},
            'all': {}
        }
        self.journal = []

        if self.number > 0:
            self.ancestor_hashes = [self.prevhash]
        else:
            self.ancestor_hashes = [None] * 256

        # do some consistency checks on parent if given
        if parent:
            if hasattr(parent, 'db') and self.db != parent.db:
                raise ValueError("Parent lives in different database")
            if self.prevhash != parent.header.hash:
                raise ValueError("Block's prevhash and parent's hash do not match")
            if self.number != parent.header.number + 1:
                raise ValueError("Block's number is not the successor of its parent number")
            if not check_gaslimit(parent, self.gas_limit):
                raise ValueError("Block's gaslimit is inconsistent with its parent's gaslimit")
            if self.difficulty != calc_difficulty(parent, self.timestamp):
                raise ValueError("Block's difficulty is inconsistent with its parent's difficulty")

        for uncle in uncles:
            assert isinstance(uncle, BlockHeader)

        original_values = {
            'gas_used': header.gas_used,
            'timestamp': header.timestamp,
            'difficulty': header.difficulty,
            'uncles_hash': header.uncles_hash,
            'bloom': header.bloom,
        }

        self.transactions = Trie(db, trie.BLANK_ROOT)
        self.receipts = Trie(db, trie.BLANK_ROOT)
        # replay transactions if state is unknown
        state_unknown = (header.prevhash != GENESIS_PREVHASH and
                         header.state_root != trie.BLANK_ROOT and
                         (len(header.state_root) != 32 or
                          b'validated:' + self.hash not in db) and
                         not making)
        if state_unknown:
            assert transaction_list is not None
            if not parent:
                parent = self.get_parent_header()
            self.state = SecureTrie(Trie(db, parent.state_root))
            self.transaction_count = 0
            self.gas_used = 0
            # replay
            for tx in transaction_list:
                success, output = processblock.apply_transaction(self, tx)
            self.finalize()
        else:
            # trust the state root in the header
            self.state = SecureTrie(Trie(self.db, header._state_root))
            self.transaction_count = 0
            if transaction_list:
                for tx in transaction_list:
                    self.add_transaction_to_list(tx)
            if self.transactions.root_hash != header.tx_list_root:
                raise ValueError("Transaction list root hash does not match")
            # receipts trie populated by add_transaction_to_list is incorrect
            # (it doesn't know intermediate states), so reset it
            self.receipts = Trie(self.db, header.receipts_root)

        if self.number < 40000:
            assert len(self.transactions) == 0

        # checks ##############################

        def must(what, f, symb, a, b):
            if not f(a, b):
                if dump_block_on_failed_verification:
                    sys.stderr.write('%r' % self.to_dict())
                raise VerificationFailed(what, a, symb, b)

        def must_equal(what, a, b):
            return must(what, lambda x, y: x == y, "==", a, b)

        def must_ge(what, a, b):
            return must(what, lambda x, y: x >= y, ">=", a, b)

        def must_le(what, a, b):
            return must(what, lambda x, y: x <= y, "<=", a, b)

        if parent:
            must_equal('prev_hash', self.prevhash, parent.hash)
            must_ge('gas_limit', self.gas_limit,
                    parent.gas_limit * (GASLIMIT_ADJMAX_FACTOR - 1) // GASLIMIT_ADJMAX_FACTOR)
            must_le('gas_limit', self.gas_limit,
                    parent.gas_limit * (GASLIMIT_ADJMAX_FACTOR + 1) // GASLIMIT_ADJMAX_FACTOR)
        must_equal('gas_used', original_values['gas_used'], self.gas_used)
        must_equal('timestamp', self.timestamp, original_values['timestamp'])
        must_equal('difficulty', self.difficulty, original_values['difficulty'])
        must_equal('uncles_hash', utils.sha3(rlp.encode(uncles)), original_values['uncles_hash'])
        assert header.block is None
        must_equal('state_root', self.state.root_hash, header.state_root)
        must_equal('tx_list_root', self.transactions.root_hash, header.tx_list_root)
        must_equal('receipts_root', self.receipts.root_hash, header.receipts_root)
        must_equal('bloom', self.bloom, original_values['bloom'])

        # from now on, trie roots refer to block instead of header
        header.block = self

        # Basic consistency verifications
        if not self.check_fields():
            raise ValueError("Block is invalid")
        if len(self.header.extra_data) > 1024:
            raise ValueError("Extra data cannot exceed 1024 bytes")
        if self.header.coinbase == '':
            raise ValueError("Coinbase cannot be empty address")
        if not self.state.root_hash_valid():
            raise ValueError("State Merkle root of block %r not found in "
                             "database" % self)
        if (not self.is_genesis() and self.nonce and not self.header.check_pow()):
            raise ValueError("PoW check failed")
        self.db.put(b'validated:' + self.hash, '1')
Example #9
0
class Block(rlp.Serializable):

    """A block.

    All attributes from the block header are accessible via properties
    (i.e. ``block.prevhash`` is equivalent to ``block.header.prevhash``). It
    is ensured that no discrepancies between header and block occur.

    :param header: the block header
    :param transaction_list: a list of transactions which are replayed if the
                             state given by the header is not known. If the
                             state is known, `None` can be used instead of the
                             empty list.
    :param uncles: a list of the headers of the uncles of this block
    :param db: the database in which the block's  state, transactions and
               receipts are stored (required)
    :param parent: optional parent which if not given may have to be loaded from
                   the database for replay
    """

    fields = [
        ('header', BlockHeader),
        ('transaction_list', CountableList(Transaction)),
        ('uncles', CountableList(BlockHeader))
    ]

    def __init__(self, header, transaction_list=[], uncles=[], db=None,
                 parent=None, making=False):
        if db is None:
            raise TypeError("No database object given")
        self.db = db
        self.header = header
        self.uncles = uncles

        self.uncles = uncles
        self.suicides = []
        self.logs = []
        self.log_listeners = []
        self.refunds = 0

        self.ether_delta = 0

        # Journaling cache for state tree updates
        self.caches = {
            'balance': {},
            'nonce': {},
            'code': {},
            'storage': {},
            'all': {}
        }
        self.journal = []

        if self.number > 0:
            self.ancestor_hashes = [self.prevhash]
        else:
            self.ancestor_hashes = [None] * 256

        # do some consistency checks on parent if given
        if parent:
            if hasattr(parent, 'db') and self.db != parent.db:
                raise ValueError("Parent lives in different database")
            if self.prevhash != parent.header.hash:
                raise ValueError("Block's prevhash and parent's hash do not match")
            if self.number != parent.header.number + 1:
                raise ValueError("Block's number is not the successor of its parent number")
            if not check_gaslimit(parent, self.gas_limit):
                raise ValueError("Block's gaslimit is inconsistent with its parent's gaslimit")
            if self.difficulty != calc_difficulty(parent, self.timestamp):
                raise ValueError("Block's difficulty is inconsistent with its parent's difficulty")

        for uncle in uncles:
            assert isinstance(uncle, BlockHeader)

        original_values = {
            'gas_used': header.gas_used,
            'timestamp': header.timestamp,
            'difficulty': header.difficulty,
            'uncles_hash': header.uncles_hash,
            'bloom': header.bloom,
        }

        self.transactions = Trie(db, trie.BLANK_ROOT)
        self.receipts = Trie(db, trie.BLANK_ROOT)
        # replay transactions if state is unknown
        state_unknown = (header.prevhash != GENESIS_PREVHASH and
                         header.state_root != trie.BLANK_ROOT and
                         (len(header.state_root) != 32 or
                          b'validated:' + self.hash not in db) and
                         not making)
        if state_unknown:
            assert transaction_list is not None
            if not parent:
                parent = self.get_parent_header()
            self.state = SecureTrie(Trie(db, parent.state_root))
            self.transaction_count = 0
            self.gas_used = 0
            # replay
            for tx in transaction_list:
                success, output = processblock.apply_transaction(self, tx)
            self.finalize()
        else:
            # trust the state root in the header
            self.state = SecureTrie(Trie(self.db, header._state_root))
            self.transaction_count = 0
            if transaction_list:
                for tx in transaction_list:
                    self.add_transaction_to_list(tx)
            if self.transactions.root_hash != header.tx_list_root:
                raise ValueError("Transaction list root hash does not match")
            # receipts trie populated by add_transaction_to_list is incorrect
            # (it doesn't know intermediate states), so reset it
            self.receipts = Trie(self.db, header.receipts_root)

        if self.number < 40000:
            assert len(self.transactions) == 0

        # checks ##############################

        def must(what, f, symb, a, b):
            if not f(a, b):
                if dump_block_on_failed_verification:
                    sys.stderr.write('%r' % self.to_dict())
                raise VerificationFailed(what, a, symb, b)

        def must_equal(what, a, b):
            return must(what, lambda x, y: x == y, "==", a, b)

        def must_ge(what, a, b):
            return must(what, lambda x, y: x >= y, ">=", a, b)

        def must_le(what, a, b):
            return must(what, lambda x, y: x <= y, "<=", a, b)

        if parent:
            must_equal('prev_hash', self.prevhash, parent.hash)
            must_ge('gas_limit', self.gas_limit,
                    parent.gas_limit * (GASLIMIT_ADJMAX_FACTOR - 1) // GASLIMIT_ADJMAX_FACTOR)
            must_le('gas_limit', self.gas_limit,
                    parent.gas_limit * (GASLIMIT_ADJMAX_FACTOR + 1) // GASLIMIT_ADJMAX_FACTOR)
        must_equal('gas_used', original_values['gas_used'], self.gas_used)
        must_equal('timestamp', self.timestamp, original_values['timestamp'])
        must_equal('difficulty', self.difficulty, original_values['difficulty'])
        must_equal('uncles_hash', utils.sha3(rlp.encode(uncles)), original_values['uncles_hash'])
        assert header.block is None
        must_equal('state_root', self.state.root_hash, header.state_root)
        must_equal('tx_list_root', self.transactions.root_hash, header.tx_list_root)
        must_equal('receipts_root', self.receipts.root_hash, header.receipts_root)
        must_equal('bloom', self.bloom, original_values['bloom'])

        # from now on, trie roots refer to block instead of header
        header.block = self

        # Basic consistency verifications
        if not self.check_fields():
            raise ValueError("Block is invalid")
        if len(self.header.extra_data) > 1024:
            raise ValueError("Extra data cannot exceed 1024 bytes")
        if self.header.coinbase == '':
            raise ValueError("Coinbase cannot be empty address")
        if not self.state.root_hash_valid():
            raise ValueError("State Merkle root of block %r not found in "
                             "database" % self)
        if (not self.is_genesis() and self.nonce and not self.header.check_pow()):
            raise ValueError("PoW check failed")
        self.db.put(b'validated:' + self.hash, '1')

    @classmethod
    def init_from_header(cls, header_rlp, db):
        """Create a block without specifying transactions or uncles.

        :param header_rlp: the RLP encoded block header
        :param db: the database for the block
        """
        header = rlp.decode(header_rlp, BlockHeader, db=db)
        return cls(header, None, [], db=db)

    @classmethod
    def init_from_parent(cls, parent, coinbase, nonce=b'', extra_data=b'',
                         timestamp=int(time.time()), uncles=[]):
        """Create a new block based on a parent block.

        The block will not include any transactions and will not be finalized.
        """
        header = BlockHeader(prevhash=parent.hash,
                             uncles_hash=utils.sha3(rlp.encode(uncles)),
                             coinbase=coinbase,
                             state_root=parent.state_root,
                             tx_list_root=trie.BLANK_ROOT,
                             receipts_root=trie.BLANK_ROOT,
                             bloom=0,
                             difficulty=calc_difficulty(parent, timestamp),
                             mixhash='',
                             number=parent.number + 1,
                             gas_limit=calc_gaslimit(parent),
                             gas_used=0,
                             timestamp=timestamp,
                             extra_data=extra_data,
                             nonce=nonce)
        block = Block(header, [], uncles, db=parent.db,
                      parent=parent, making=True)
        block.ancestor_hashes = [parent.hash] + parent.ancestor_hashes
        return block

    def check_fields(self):
        """Check that the values of all fields are well formed."""
        # serialize and deserialize and check that the values didn't change
        l = Block.serialize(self)
        return rlp.decode(rlp.encode(l)) == l

    @property
    def hash(self):
        """The binary block hash

        This is equivalent to ``header.hash``.
        """
        return utils.sha3(rlp.encode(self.header))

    def hex_hash(self):
        """The hex encoded block hash.

        This is equivalent to ``header.hex_hash().
        """
        return encode_hex(self.hash)

    @property
    def tx_list_root(self):
        return self.transactions.root_hash

    @tx_list_root.setter
    def tx_list_root(self, value):
        self.transactions = Trie(self.db, value)

    @property
    def receipts_root(self):
        return self.receipts.root_hash

    @receipts_root.setter
    def receipts_root(self, value):
        self.receipts = Trie(self.db, value)

    @property
    def state_root(self):
        self.commit_state()
        return self.state.root_hash

    @state_root.setter
    def state_root(self, value):
        self.state = SecureTrie(Trie(self.db, value))
        self.reset_cache()

    @property
    def uncles_hash(self):
        return utils.sha3(rlp.encode(self.uncles))

    @property
    def transaction_list(self):
        txs = []
        for i in range(self.transaction_count):
            txs.append(self.get_transaction(i))
        return txs

    def validate_uncles(self):
        """Validate the uncles of this block."""
        if utils.sha3(rlp.encode(self.uncles)) != self.uncles_hash:
            return False
        if len(self.uncles) > MAX_UNCLES:
            return False
        for uncle in self.uncles:
            assert uncle.prevhash in self.db
            if uncle.number == self.number:
                log.error("uncle at same block height", block=self)
                return False

        # Check uncle validity
        ancestor_chain = [self] + [a for a in self.get_ancestor_list(MAX_UNCLE_DEPTH + 1) if a]
        assert len(ancestor_chain) == min(self.header.number + 1, MAX_UNCLE_DEPTH + 2)
        ineligible = []
        # Uncles of this block cannot be direct ancestors and cannot also
        # be uncles included 1-6 blocks ago
        for ancestor in ancestor_chain[1:]:
            ineligible.extend(ancestor.uncles)
        ineligible.extend([b.header for b in ancestor_chain])
        eligible_ancestor_hashes = [x.hash for x in ancestor_chain[2:]]
        for uncle in self.uncles:
            if not uncle.check_pow():
                return False
            if uncle.prevhash not in eligible_ancestor_hashes:
                log.error("Uncle does not have a valid ancestor", block=self,
                          eligible=[x.encode('hex') for x in eligible_ancestor_hashes],
                          uncle_prevhash=uncle.prevhash.encode('hex'))
                return False
            if uncle in ineligible:
                log.error("Duplicate uncle", block=self,
                          uncle=encode_hex(utils.sha3(rlp.encode(uncle))))
                return False
            ineligible.append(uncle)
        return True

    def get_ancestor_list(self, n):
        """Return `n` ancestors of this block.

        :returns: a list [p(self), p(p(self)), ..., p^n(self)]
        """
        if n == 0 or self.header.number == 0:
            return []
        p = self.get_parent()
        return [p] + p.get_ancestor_list(n-1)

    def get_ancestor_hash(self, n):
        assert n > 0
        while len(self.ancestor_hashes) < n:
            if self.number == len(self.ancestor_hashes) - 1:
                self.ancestor_hashes.append(None)
            else:
                self.ancestor_hashes.append(
                    get_block(self.db,
                              self.ancestor_hashes[-1]).get_parent().hash)
        return self.ancestor_hashes[n-1]

    def get_ancestor(self, n):
        return self.get_block(self.get_ancestor_hash(n))

    def is_genesis(self):
        """`True` if this block is the genesis block, otherwise `False`."""
        return self.header.number == 0

    def _get_acct(self, address):
        """Get the account with the given address.

        Note that this method ignores cached account items.
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20 or len(address) == 0
        rlpdata = self.state.get(address)
        if rlpdata != trie.BLANK_NODE:
            acct = rlp.decode(rlpdata, Account, db=self.db)
        else:
            acct = Account.blank_account(self.db)
        return acct

    def _get_acct_item(self, address, param):
        """Get a specific parameter of a specific account.

        :param address: the address of the account (binary or hex string)
        :param param: the requested parameter (`'nonce'`, `'balance'`,
                      `'storage'` or `'code'`)
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20 or len(address) == 0
        if address in self.caches[param]:
            return self.caches[param][address]
        else:
            account = self._get_acct(address)
            o = getattr(account, param)
            self.caches[param][address] = o
            return o

    def _set_acct_item(self, address, param, value):
        """Set a specific parameter of a specific account.

        :param address: the address of the account (binary or hex string)
        :param param: the requested parameter (`'nonce'`, `'balance'`,
                      `'storage'` or `'code'`)
        :param value: the new value
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20
        self.set_and_journal(param, address, value)
        self.set_and_journal('all', address, True)

    def set_and_journal(self, cache, index, value):
        prev = self.caches[cache].get(index, None)
        if prev != value:
            self.journal.append([cache, index, prev, value])
            self.caches[cache][index] = value

    def _delta_item(self, address, param, value):
        """Add a value to an account item.

        If the resulting value would be negative, it is left unchanged and
        `False` is returned.

        :param address: the address of the account (binary or hex string)
        :param param: the parameter to increase or decrease (`'nonce'`,
                      `'balance'`, `'storage'` or `'code'`)
        :param value: can be positive or negative
        :returns: `True` if the operation was successful, `False` if not
        """
        new_value = self._get_acct_item(address, param) + value
        if new_value < 0:
            return False
        self._set_acct_item(address, param, new_value % 2**256)
        return True

    def mk_transaction_receipt(self, tx):
        """Create a receipt for a transaction."""
        return Receipt(self.state_root, self.gas_used, self.logs)

    def add_transaction_to_list(self, tx):
        """Add a transaction to the transaction trie.

        Note that this does not execute anything, i.e. the state is not
        updated.
        """
        k = rlp.encode(self.transaction_count)
        self.transactions.update(k, rlp.encode(tx))
        r = self.mk_transaction_receipt(tx)
        self.receipts.update(k, rlp.encode(r))
        self.bloom |= r.bloom  # int
        self.transaction_count += 1

    def get_transaction(self, num):
        """Get the `num`th transaction in this block.

        :raises: :exc:`IndexError` if the transaction does not exist
        """
        index = rlp.encode(num)
        tx = self.transactions.get(index)
        if tx == trie.BLANK_NODE:
            raise IndexError('Transaction does not exist')
        else:
            return rlp.decode(tx, Transaction)

    _get_transactions_cache = None

    def get_transactions(self):
        """Build a list of all transactions in this block."""
        num = self.transaction_count
        if not self._get_transactions_cache or len(self._get_transactions_cache) != num:
            txs = []
            for i in range(num):
                txs.append(self.get_transaction(i))
            self._get_transactions_cache = txs
        return self._get_transactions_cache

    def get_transaction_hashes(self):
        "helper to check if blk contains a tx"
        return [utils.sha3(self.transactions.get(rlp.encode(i)))
                for i in range(self.transaction_count)]

    def includes_transaction(self, tx_hash):
        assert isinstance(tx_hash, bytes)
        #assert self.get_transaction_hashes() == [tx.hash for tx in self.get_transactions()]
        return tx_hash in self.get_transaction_hashes()

    def get_receipt(self, num):
        """Get the receipt of the `num`th transaction.

        :returns: an instance of :class:`Receipt`
        """
        index = rlp.encode(num)
        receipt = self.receipts.get(index)
        if receipt == trie.BLANK_NODE:
            raise IndexError('Receipt does not exist')
        else:
            return rlp.decode(receipt, Receipt)

    def get_receipts(self):
        """Build a list of all receipts in this block."""
        receipts = []
        for i in count():
            try:
                receipts.append(self.get_receipt(i))
            except IndexError:
                return receipts

    def get_nonce(self, address):
        """Get the nonce of an account.

        :param address: the address of the account (binary or hex string)
        """
        return self._get_acct_item(address, 'nonce')

    def set_nonce(self, address, value):
        """Set the nonce of an account.

        :param address: the address of the account (binary or hex string)
        :param value: the new nonce
        :returns: `True` if successful, otherwise `False`
        """
        return self._set_acct_item(address, 'nonce', value)

    def increment_nonce(self, address):
        """Increment the nonce of an account.

        :param address: the address of the account (binary or hex string)
        :returns: `True` if successful, otherwise `False`
        """
        return self._delta_item(address, 'nonce', 1)

    def decrement_nonce(self, address):
        """Decrement the nonce of an account.

        :param address: the address of the account (binary or hex string)
        :returns: `True` if successful, otherwise `False`
        """
        return self._delta_item(address, 'nonce', -1)

    def get_balance(self, address):
        """Get the balance of an account.

        :param address: the address of the account (binary or hex string)
        """
        return self._get_acct_item(address, 'balance')

    def set_balance(self, address, value):
        """Set the balance of an account.

        :param address: the address of the account (binary or hex string)
        :param value: the new balance
        :returns: `True` if successful, otherwise `False`
        """
        self._set_acct_item(address, 'balance', value)

    def delta_balance(self, address, value):
        """Increase the balance of an account.

        :param address: the address of the account (binary or hex string)
        :param value: can be positive or negative
        :returns: `True` if successful, otherwise `False`
        """
        return self._delta_item(address, 'balance', value)

    def transfer_value(self, from_addr, to_addr, value):
        """Transfer a value between two account balances.

        :param from_addr: the address of the sending account (binary or hex
                          string)
        :param to_addr: the address of the receiving account (binary or hex
                        string)
        :param value: the (positive) value to send
        :returns: `True` if successful, otherwise `False`
        """
        assert value >= 0
        if self.delta_balance(from_addr, -value):
            return self.delta_balance(to_addr, value)
        return False

    def get_code(self, address):
        """Get the code of an account.

        :param address: the address of the account (binary or hex string)
        """
        return self._get_acct_item(address, 'code')

    def set_code(self, address, value):
        """Set the code of an account.

        :param address: the address of the account (binary or hex string)
        :param value: the new code
        :returns: `True` if successful, otherwise `False`
        """
        self._set_acct_item(address, 'code', value)

    def get_storage(self, address):
        """Get the trie holding an account's storage.

        :param address: the address of the account (binary or hex string)
        :param value: the new code
        """
        storage_root = self._get_acct_item(address, 'storage')
        return SecureTrie(Trie(self.db, storage_root))

    def reset_storage(self, address):
        self._set_acct_item(address, 'storage', b'')
        CACHE_KEY = b'storage:' + address
        if CACHE_KEY in self.caches:
            for k in self.caches[CACHE_KEY]:
                self.set_and_journal(CACHE_KEY, k, 0)

    def get_storage_data(self, address, index):
        """Get a specific item in the storage of an account.

        :param address: the address of the account (binary or hex string)
        :param index: the index of the requested item in the storage
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20
        CACHE_KEY = b'storage:' + address
        if CACHE_KEY in self.caches:
            if index in self.caches[CACHE_KEY]:
                return self.caches[CACHE_KEY][index]
        key = utils.zpad(utils.coerce_to_bytes(index), 32)
        storage = self.get_storage(address).get(key)
        if storage:
            return rlp.decode(storage, big_endian_int)
        else:
            return 0

    def set_storage_data(self, address, index, value):
        """Set a specific item in the storage of an account.

        :param address: the address of the account (binary or hex string)
        :param index: the index of the item in the storage
        :param value: the new value of the item
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20
        CACHE_KEY = b'storage:' + address
        if CACHE_KEY not in self.caches:
            self.caches[CACHE_KEY] = {}
            self.set_and_journal('all', address, True)
        self.set_and_journal(CACHE_KEY, index, value)

    def account_exists(self, address):
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20
        return len(self.state.get(address)) > 0 or address in self.caches['all']

    def add_log(self, log):
        self.logs.append(log)
        for L in self.log_listeners:
            L(log)

    def commit_state(self):
        """Commit account caches"""
        """Write the acount caches on the corresponding tries."""
        changes = []
        if len(self.journal) == 0:
            # log_state.trace('delta', changes=[])
            return
        addresses = sorted(list(self.caches['all'].keys()))
        for addr in addresses:
            acct = self._get_acct(addr)

            # storage
            for field in ('balance', 'nonce', 'code', 'storage'):
                if addr in self.caches[field]:
                    v = self.caches[field][addr]
                    changes.append([field, addr, v])
                    setattr(acct, field, v)

            t = SecureTrie(Trie(self.db, acct.storage))
            for k, v in self.caches.get(b'storage:' + addr, {}).items():
                enckey = utils.zpad(utils.coerce_to_bytes(k), 32)
                val = rlp.encode(v)
                changes.append(['storage', addr, k, v])
                if v:
                    t.update(enckey, val)
                else:
                    t.delete(enckey)
            acct.storage = t.root_hash
            self.state.update(addr, rlp.encode(acct))
        log_state.trace('delta', changes=changes)
        self.reset_cache()
        self.db.put(b'validated:' + self.hash, '1')

    def del_account(self, address):
        """Delete an account.

        :param address: the address of the account (binary or hex string)
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20
        self.commit_state()
        self.state.delete(address)

    def account_to_dict(self, address, with_storage_root=False,
                        with_storage=True):
        """Serialize an account to a dictionary with human readable entries.

        :param address: the 20 bytes account address
        :param with_storage_root: include the account's storage root
        :param with_storage: include the whole account's storage
        """
        if len(address) == 40:
            address = decode_hex(address)
        assert len(address) == 20

        if with_storage_root:
            # if there are uncommited account changes the current storage root
            # is meaningless
            assert len(self.journal) == 0
        med_dict = {}

        account = self._get_acct(address)
        for field in ('balance', 'nonce'):
            value = self.caches[field].get(address, getattr(account, field))
            med_dict[field] = to_string(value)
        code = self.caches['code'].get(address, account.code)
        med_dict['code'] = b'0x' + encode_hex(code)

        storage_trie = SecureTrie(Trie(self.db, account.storage))
        if with_storage_root:
            med_dict['storage_root'] = encode_hex(storage_trie.get_root_hash())
        if with_storage:
            med_dict['storage'] = {}
            d = storage_trie.to_dict()
            subcache = self.caches.get(b'storage:' + address, {})
            subkeys = [utils.zpad(utils.coerce_to_bytes(kk), 32)
                       for kk in list(subcache.keys())]
            for k in list(d.keys()) + subkeys:
                v = d.get(k, None)
                v2 = subcache.get(utils.big_endian_to_int(k), None)
                hexkey = b'0x' + encode_hex(utils.zunpad(k))
                if v2 is not None:
                    if v2 != 0:
                        med_dict['storage'][hexkey] = \
                            b'0x' + encode_hex(utils.int_to_big_endian(v2))
                elif v is not None:
                    med_dict['storage'][hexkey] = b'0x' + encode_hex(rlp.decode(v))

        return med_dict

    def reset_cache(self):
        """Reset cache and journal without commiting any changes."""
        self.caches = {
            'all': {},
            'balance': {},
            'nonce': {},
            'code': {},
            'storage': {},
        }
        self.journal = []

    def snapshot(self):
        """Make a snapshot of the current state to enable later reverting."""
        return {
            'state': self.state.root_hash,
            'gas': self.gas_used,
            'txs': self.transactions,
            'txcount': self.transaction_count,
            'suicides': self.suicides,
            'logs': self.logs,
            'refunds': self.refunds,
            'suicides_size': len(self.suicides),
            'logs_size': len(self.logs),
            'journal': self.journal,  # pointer to reference, so is not static
            'journal_size': len(self.journal),
            'ether_delta': self.ether_delta
        }

    def revert(self, mysnapshot):
        """Revert to a previously made snapshot.

        Reverting is for example necessary when a contract runs out of gas
        during execution.
        """
        self.journal = mysnapshot['journal']
        log_state.trace('reverting')
        while len(self.journal) > mysnapshot['journal_size']:
            cache, index, prev, post = self.journal.pop()
            log_state.trace('%r %r %r %r' % (cache, index, prev, post))
            if prev is not None:
                self.caches[cache][index] = prev
            else:
                del self.caches[cache][index]
        self.suicides = mysnapshot['suicides']
        while len(self.suicides) > mysnapshot['suicides_size']:
            self.suicides.pop()
        self.logs = mysnapshot['logs']
        while len(self.logs) > mysnapshot['logs_size']:
            self.logs.pop()
        self.refunds = mysnapshot['refunds']
        self.state.root_hash = mysnapshot['state']
        self.gas_used = mysnapshot['gas']
        self.transactions = mysnapshot['txs']
        self.transaction_count = mysnapshot['txcount']
        self._get_transactions_cache = None
        self.ether_delta = mysnapshot['ether_delta']

    def finalize(self):
        """Apply rewards and commit."""
        delta = int(BLOCK_REWARD + NEPHEW_REWARD * len(self.uncles))
        self.delta_balance(self.coinbase, delta)
        self.ether_delta += delta

        for uncle in self.uncles:
            r = BLOCK_REWARD * \
                (UNCLE_DEPTH_PENALTY_FACTOR + uncle.number - self.number) \
                / UNCLE_DEPTH_PENALTY_FACTOR
            r = int(r)
            self.delta_balance(uncle.coinbase, r)
            self.ether_delta += r
        self.commit_state()

    def to_dict(self, with_state=False, full_transactions=False,
                with_storage_roots=False, with_uncles=False):
        """Serialize the block to a readable dictionary.

        :param with_state: include state for all accounts
        :param full_transactions: include serialized transactions (hashes
                                  otherwise)
        :param with_storage_roots: if account states are included also include
                                   their storage roots
        :param with_uncles: include uncle hashes
        """
        b = {"header": self.header.to_dict()}
        txlist = []
        for i, tx in enumerate(self.get_transactions()):
            receipt_rlp = self.receipts.get(rlp.encode(i))
            receipt = rlp.decode(receipt_rlp, Receipt)
            if full_transactions:
                txjson = tx.to_dict()
            else:
                txjson = tx.hash
            txlist.append({
                "tx": txjson,
                "medstate": encode_hex(receipt.state_root),
                "gas": to_string(receipt.gas_used),
                "logs": [Log.serialize(log) for log in receipt.logs],
                "bloom": utils.int256.serialize(receipt.bloom)
            })
        b["transactions"] = txlist
        if with_state:
            state_dump = {}
            for address, v in self.state.to_dict().items():
                state_dump[encode_hex(address)] = self.account_to_dict(address, with_storage_roots)
            b['state'] = state_dump
        if with_uncles:
            b['uncles'] = [self.__class__.deserialize_header(u)
                           for u in self.uncles]
        return b

    @property
    def mining_hash(self):
        return utils.sha3(rlp.encode(self.header,
                                     BlockHeader.exclude(['nonce', 'mixhash'])))

    def get_parent(self):
        """Get the parent of this block."""
        if self.number == 0:
            raise UnknownParentException('Genesis block has no parent')
        try:
            parent = get_block(self.db, self.prevhash)
        except KeyError:
            raise UnknownParentException(encode_hex(self.prevhash))
        # assert parent.state.db.db == self.state.db.db
        return parent

    def get_parent_header(self):
        """Get the parent of this block."""
        if self.number == 0:
            raise UnknownParentException('Genesis block has no parent')
        try:
            parent_header = get_block_header(self.db, self.prevhash)
        except KeyError:
            raise UnknownParentException(encode_hex(self.prevhash))
        # assert parent.state.db.db == self.state.db.db
        return parent_header

    def has_parent(self):
        """`True` if this block has a known parent, otherwise `False`."""
        try:
            self.get_parent()
            return True
        except UnknownParentException:
            return False

    def chain_difficulty(self):
        """Get the summarized difficulty.

        If the summarized difficulty is not stored in the database, it will be
        calculated recursively and put in the database.
        """
        if self.is_genesis():
            return self.difficulty
        elif b'difficulty:' + encode_hex(self.hash) in self.db:
            encoded = self.db.get(b'difficulty:' + encode_hex(self.hash))
            return utils.decode_int(encoded)
        else:
            o = self.difficulty + self.get_parent().chain_difficulty()
            # o += sum([uncle.difficulty for uncle in self.uncles])
            self.state.db.put(b'difficulty:' + encode_hex(self.hash),
                              utils.encode_int(o))
            return o

            return rlp.decode(rlp.encode(l)) == l

    def __eq__(self, other):
        """Two blocks are equal iff they have the same hash."""
        return isinstance(other, (Block, CachedBlock)) and self.hash == other.hash

    def __hash__(self):
        return utils.big_endian_to_int(self.hash)

    def __ne__(self, other):
        return not self.__eq__(other)

    def __gt__(self, other):
        return self.number > other.number

    def __lt__(self, other):
        return self.number < other.number

    def __repr__(self):
        return '<%s(#%d %s)>' % (self.__class__.__name__, self.number, encode_hex(self.hash)[:8])

    def __structlog__(self):
        return encode_hex(self.hash)