|
| 1 | +import rlp |
| 2 | +from ethereum.utils import normalize_address, hash32, trie_root, \ |
| 3 | + big_endian_int, address, int256, encode_hex, encode_int, sha3 |
| 4 | +from rlp.sedes import big_endian_int, Binary, binary, CountableList |
| 5 | +from ethereum import utils |
| 6 | +from ethereum import trie |
| 7 | +from ethereum.trie import Trie |
| 8 | +from ethereum.securetrie import SecureTrie |
| 9 | +from ethereum.config import default_config |
| 10 | +from ethereum.transactions import Transaction |
| 11 | +from ethereum.db import BaseDB |
| 12 | +import sys |
| 13 | +if sys.version_info.major == 2: |
| 14 | + from repoze.lru import lru_cache |
| 15 | +else: |
| 16 | + from functools import lru_cache |
| 17 | + |
| 18 | + |
| 19 | +class BlockHeader(rlp.Serializable): |
| 20 | + |
| 21 | + """A block header. |
| 22 | +
|
| 23 | + If the block with this header exists as an instance of :class:`Block`, the |
| 24 | + connection can be made explicit by setting :attr:`BlockHeader.block`. Then, |
| 25 | + :attr:`BlockHeader.state_root`, :attr:`BlockHeader.tx_list_root` and |
| 26 | + :attr:`BlockHeader.receipts_root` always refer to the up-to-date value in |
| 27 | + the block instance. |
| 28 | +
|
| 29 | + :ivar block: an instance of :class:`Block` or `None` |
| 30 | + :ivar prevhash: the 32 byte hash of the previous block |
| 31 | + :ivar uncles_hash: the 32 byte hash of the RLP encoded list of uncle |
| 32 | + headers |
| 33 | + :ivar coinbase: the 20 byte coinbase address |
| 34 | + :ivar state_root: the root of the block's state trie |
| 35 | + :ivar tx_list_root: the root of the block's transaction trie |
| 36 | + :ivar receipts_root: the root of the block's receipts trie |
| 37 | + :ivar bloom: TODO |
| 38 | + :ivar difficulty: the block's difficulty |
| 39 | + :ivar number: the number of ancestors of this block (0 for the genesis |
| 40 | + block) |
| 41 | + :ivar gas_limit: the block's gas limit |
| 42 | + :ivar gas_used: the total amount of gas used by all transactions in this |
| 43 | + block |
| 44 | + :ivar timestamp: a UNIX timestamp |
| 45 | + :ivar extra_data: up to 1024 bytes of additional data |
| 46 | + :ivar nonce: a 32 byte nonce constituting a proof-of-work, or the empty |
| 47 | + string as a placeholder |
| 48 | + """ |
| 49 | + |
| 50 | + fields = [ |
| 51 | + ('prevhash', hash32), |
| 52 | + ('uncles_hash', hash32), |
| 53 | + ('coinbase', address), |
| 54 | + ('state_root', trie_root), |
| 55 | + ('tx_list_root', trie_root), |
| 56 | + ('receipts_root', trie_root), |
| 57 | + ('bloom', int256), |
| 58 | + ('difficulty', big_endian_int), |
| 59 | + ('number', big_endian_int), |
| 60 | + ('gas_limit', big_endian_int), |
| 61 | + ('gas_used', big_endian_int), |
| 62 | + ('timestamp', big_endian_int), |
| 63 | + ('extra_data', binary), |
| 64 | + ('mixhash', binary), |
| 65 | + ('nonce', binary) |
| 66 | + ] |
| 67 | + |
| 68 | + def __init__(self, |
| 69 | + prevhash=default_config['GENESIS_PREVHASH'], |
| 70 | + uncles_hash=utils.sha3rlp([]), |
| 71 | + coinbase=default_config['GENESIS_COINBASE'], |
| 72 | + state_root=trie.BLANK_ROOT, |
| 73 | + tx_list_root=trie.BLANK_ROOT, |
| 74 | + receipts_root=trie.BLANK_ROOT, |
| 75 | + bloom=0, |
| 76 | + difficulty=default_config['GENESIS_DIFFICULTY'], |
| 77 | + number=0, |
| 78 | + gas_limit=default_config['GENESIS_GAS_LIMIT'], |
| 79 | + gas_used=0, |
| 80 | + timestamp=0, |
| 81 | + extra_data='', |
| 82 | + mixhash=default_config['GENESIS_MIXHASH'], |
| 83 | + nonce=''): |
| 84 | + # at the beginning of a method, locals() is a dict of all arguments |
| 85 | + fields = {k: v for k, v in locals().items() if k != 'self'} |
| 86 | + if len(fields['coinbase']) == 40: |
| 87 | + fields['coinbase'] = decode_hex(fields['coinbase']) |
| 88 | + assert len(fields['coinbase']) == 20 |
| 89 | + self.block = None |
| 90 | + super(BlockHeader, self).__init__(**fields) |
| 91 | + |
| 92 | + @property |
| 93 | + def hash(self): |
| 94 | + """The binary block hash""" |
| 95 | + return utils.sha3(rlp.encode(self)) |
| 96 | + |
| 97 | + @property |
| 98 | + def hex_hash(self): |
| 99 | + return encode_hex(self.hash) |
| 100 | + |
| 101 | + @property |
| 102 | + def mining_hash(self): |
| 103 | + return utils.sha3(rlp.encode(self, BlockHeader.exclude(['mixhash', 'nonce']))) |
| 104 | + |
| 105 | + @property |
| 106 | + def signing_hash(self): |
| 107 | + return utils.sha3(rlp.encode(self, BlockHeader.exclude(['extra_data']))) |
| 108 | + |
| 109 | + def to_dict(self): |
| 110 | + """Serialize the header to a readable dictionary.""" |
| 111 | + d = {} |
| 112 | + for field in ('prevhash', 'uncles_hash', 'extra_data', 'nonce', |
| 113 | + 'mixhash'): |
| 114 | + d[field] = b'0x' + encode_hex(getattr(self, field)) |
| 115 | + for field in ('state_root', 'tx_list_root', 'receipts_root', |
| 116 | + 'coinbase'): |
| 117 | + d[field] = encode_hex(getattr(self, field)) |
| 118 | + for field in ('number', 'difficulty', 'gas_limit', 'gas_used', |
| 119 | + 'timestamp'): |
| 120 | + d[field] = to_string(getattr(self, field)) |
| 121 | + d['bloom'] = encode_hex(int256.serialize(self.bloom)) |
| 122 | + assert len(d) == len(BlockHeader.fields) |
| 123 | + return d |
| 124 | + |
| 125 | + def __repr__(self): |
| 126 | + return '<%s(#%d %s)>' % (self.__class__.__name__, self.number, |
| 127 | + encode_hex(self.hash)[:8]) |
| 128 | + |
| 129 | + def __eq__(self, other): |
| 130 | + """Two blockheader are equal iff they have the same hash.""" |
| 131 | + return isinstance(other, BlockHeader) and self.hash == other.hash |
| 132 | + |
| 133 | + def __hash__(self): |
| 134 | + return utils.big_endian_to_int(self.hash) |
| 135 | + |
| 136 | + def __ne__(self, other): |
| 137 | + return not self.__eq__(other) |
| 138 | + |
| 139 | + |
| 140 | +class Block(rlp.Serializable): |
| 141 | + |
| 142 | + """A block. |
| 143 | +
|
| 144 | + All attributes from the block header are accessible via properties |
| 145 | + (i.e. ``block.prevhash`` is equivalent to ``block.header.prevhash``). It |
| 146 | + is ensured that no discrepancies between header and block occur. |
| 147 | +
|
| 148 | + :param header: the block header |
| 149 | + :param transactions: a list of transactions which are replayed if the |
| 150 | + state given by the header is not known. If the |
| 151 | + state is known, `None` can be used instead of the |
| 152 | + empty list. |
| 153 | + :param uncles: a list of the headers of the uncles of this block |
| 154 | + :param db: the database in which the block's state, transactions and |
| 155 | + receipts are stored (required) |
| 156 | + :param parent: optional parent which if not given may have to be loaded from |
| 157 | + the database for replay |
| 158 | + """ |
| 159 | + |
| 160 | + fields = [ |
| 161 | + ('header', BlockHeader), |
| 162 | + ('transactions', CountableList(Transaction)), |
| 163 | + ('uncles', CountableList(BlockHeader)) |
| 164 | + ] |
| 165 | + |
| 166 | + def __init__(self, header, transactions=None, uncles=None, db=None): |
| 167 | + # assert isinstance(db, BaseDB), "No database object given" |
| 168 | + # self.db = db |
| 169 | + |
| 170 | + self.header = header |
| 171 | + self.transactions = transactions or [] |
| 172 | + self.uncles = uncles or [] |
| 173 | + self.uncles = list(self.uncles) |
| 174 | + |
| 175 | + def __getattribute__(self, name): |
| 176 | + try: |
| 177 | + return rlp.Serializable.__getattribute__(self, name) |
| 178 | + except AttributeError: |
| 179 | + return getattr(self.header, name) |
| 180 | + |
| 181 | + # TODO: remove chain_difficulty mock |
| 182 | + def chain_difficulty(self): |
| 183 | + return self.header.number + 1 |
| 184 | + |
| 185 | + @property |
| 186 | + def transaction_count(self): |
| 187 | + return len(self.transactions) |
| 188 | + |
| 189 | + |
| 190 | +BLANK_UNCLES_HASH = sha3(rlp.encode([])) |
| 191 | + |
| 192 | + |
| 193 | +class FakeHeader(): |
| 194 | + |
| 195 | + def __init__(self, hash='\x00' * 32, number=0, timestamp=0, difficulty=1, gas_limit=3141592, gas_used=0, uncles_hash=BLANK_UNCLES_HASH): |
| 196 | + self.hash = hash |
| 197 | + self.number = number |
| 198 | + self.timestamp = timestamp |
| 199 | + self.difficulty = difficulty |
| 200 | + self.gas_limit = gas_limit |
| 201 | + self.gas_used = gas_used |
| 202 | + self.uncles_hash = uncles_hash |
0 commit comments