class StateDataset: def __init__(self, db, state_root): self.db = db if isinstance(state_root, str): self.state_root = decode_hex(state_root) else: self.state_root = state_root try: self.trie = Trie(db, self.state_root) self.is_in_db = True except KeyError: self.state_root = None self.is_in_db = False logging.warning('State root %s not in database', self.state_root) logging.info('State created') def to_dict(self): state_dict = dict() for k in self.trie: try: acc = Account.from_trie(self.db, k, self.trie[k]) except KeyError: logging.error('value in trie for %s not found', k) state_dict[acc.address] = (acc.nonce, acc.balance, acc.storage_root, acc.contract_code, acc.is_address_in_db) return state_dict def to_panda_dataframe(self): trie_dict = self.trie.to_dict() size = len(trie_dict) dtype = [('sha3_account', np.str, ACCOUNT_LENGTH), ('account', np.str, ACCOUNT_LENGTH), ('nonce', np.float), ('balance', np.float), ('is_contract', np.bool), ('code_size', np.float), ('storage_size', np.float), ('key_in_db', np.bool)] arr = np.zeros(size, dtype=dtype) i = 0 for k in self.trie: try: account = Account.from_trie(self.db, k, self.trie[k]) except KeyError: logging.error('value in trie for %s not found', k) continue arr[i] = (encode_hex(k), account.address, account.nonce, account.balance, account.is_contract, account.code_size(self.db), account.storage_size(self.db), account.is_address_in_db) i += 1 df = pd.DataFrame.from_records(arr, index='sha3_account') return df def get_account(self, address): key = utils.sha3(to_canonical_address(address)) try: rlp_data = self.trie.get(key) acc = Account.from_trie(self.db, key, rlp_data) except KeyError: acc = Account.notFound(address) return acc
def __init__(self, db, root): """ :param db: :param root: """ self.db = db self.trie = Trie(self.db, root) self.secure_trie = SecureTrie(self.trie) self.journal = [] self.cache = {}
def __init__(self, db, state_root): self.db = db if isinstance(state_root, str): self.state_root = decode_hex(state_root) else: self.state_root = state_root try: self.trie = Trie(db, self.state_root) self.is_in_db = True except KeyError: self.state_root = None self.is_in_db = False logging.warning('State root %s not in database', self.state_root) logging.info('State created')
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 = []
def storage_size(self, db): size = 0 if self.is_contract: try: storage_trie = Trie(db, decode_hex(self.storage_root)) except KeyError: logging.warning('storage root %s not in database', self.storage_root) return size try: trie_dict = storage_trie.to_dict() except KeyError: logging.warning('storage root %s integrity error in database', self.storage_root) return size size = len(trie_dict) return size
def __init__(self, root=b'', env=Env(), executing_on_head=False, **kwargs): self.env = env self.trie = SecureTrie(Trie(RefcountDB(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 = [] self.deletes = [] self.changed = {} self.executing_on_head = executing_on_head
def __init__(self, nonce, balance, storage, code_hash, db, address): self.db = db self.address = address 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
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
class State: """adjusted state from ethereum.state.""" def __init__(self, db, root): """ :param db: :param root: """ self.db = db self.trie = Trie(self.db, root) self.secure_trie = SecureTrie(self.trie) self.journal = [] self.cache = {} def get_and_cache_account(self, addr): """Gets and caches an account for an addres, creates blank if not found. :param addr: :return: """ 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=addr) else: o = Account.blank_account(self.db, addr, 0) self.cache[addr] = o o._mutable = True o._cached_rlp = None return o def get_all_accounts(self): """iterates through trie to and yields non-blank leafs as accounts.""" for address_hash, rlpdata in self.secure_trie.trie.iter_branch(): if rlpdata != trie.BLANK_NODE: yield rlp.decode(rlpdata, Account, db=self.db, address=address_hash)
def __init__(self, nonce, balance, storage, code_hash, env, address): assert isinstance(env.db, BaseDB) self.env = env self.address = address acc = _Account(nonce, balance, storage, code_hash) self.nonce = acc.nonce self.balance = acc.balance self.storage = acc.storage self.code_hash = acc.code_hash self.storage_cache = {} self.storage_trie = SecureTrie(Trie(RefcountDB(self.env.db))) self.storage_trie.root_hash = self.storage self.touched = False self.existent_at_start = True self._mutable = True self.deleted = False
class State(): ''' adjusted state from ethereum.state ''' def __init__(self, db, root): self.db = db self.trie = Trie(self.db, root) self.secureTrie = SecureTrie(self.trie) 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.secureTrie.get(address) if rlpdata == trie.BLANK_NODE and len( address) == 32: # support for hashed addresses rlpdata = self.trie.get(address) if rlpdata != trie.BLANK_NODE: o = rlp.decode(rlpdata, Account, db=self.db, address=address) else: o = Account.blank_account(self.db, address, 0) self.cache[address] = o o._mutable = True o._cached_rlp = None return o def get_all_accounts(self): ''' iterates through trie to get all items ''' accounts = [] for addressHash, rlpdata in self.secureTrie.trie.to_dict().items(): if rlpdata != trie.BLANK_NODE: accounts.append( rlp.decode(rlpdata, Account, db=self.db, address=addressHash)) return accounts
def __init__(self, db, root): self.db = db self.trie = Trie(self.db, root) self.secure_trie = SecureTrie(self.trie) self.journal = [] self.cache = {}
def receipts_root(self, value): self.receipts = Trie(self.db, value)
def tx_list_root(self, value): self.transactions = Trie(self.db, value)
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')
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)