ethereum.genesis
Types and functions for beginning a new chain.
Genesis is the term for the beginning of a new chain, and so a genesis block
is a block with no parent (its parent_hash is all zeros.)
The genesis configuration for a chain is specified with a
GenesisConfiguration, and genesis blocks are created with
add_genesis_block.
GenesisConfiguration
Configuration for the first block of an Ethereum chain.
Specifies the allocation of ether set out in the pre-sale, and some of the fields of the genesis block.
| 37 | @slotted_freezable |
|---|
| 38 | @dataclass |
|---|
class GenesisConfiguration:
chain_id
Discriminant between diverged blockchains; 1 for Ethereum's main network.
| 47 | chain_id: U64 |
|---|
difficulty
See difficulty (and subsequent forks.)
| 52 | difficulty: Uint |
|---|
extra_data
See extra_data (and subsequent forks.)
| 59 | extra_data: Bytes |
|---|
gas_limit
See gas_limit (and subsequent forks.)
| 66 | gas_limit: Uint |
|---|
nonce
See nonce (and subsequent forks.)
| 73 | nonce: Bytes8 |
|---|
timestamp
See timestamp (and subsequent forks.)
| 80 | timestamp: U256 |
|---|
initial_accounts
State of the blockchain at genesis.
| 87 | initial_accounts: Dict[str, Dict] |
|---|
get_genesis_configuration
Read a genesis configuration from the given JSON file path.
The genesis file should be present in the assets directory.
def get_genesis_configuration(genesis_file: str) -> GenesisConfiguration:
| 94 | """ |
|---|---|
| 95 | Read a genesis configuration from the given JSON file path. |
| 96 | |
| 97 | The genesis file should be present in the `assets` directory. |
| 98 | """ |
| 99 | genesis_path = f"assets/{genesis_file}" |
| 100 | genesis_bytes = pkgutil.get_data("ethereum", genesis_path) |
| 101 | if genesis_bytes is None: |
| 102 | raise Exception(f"Unable to read genesis from `{genesis_path}`") |
| 103 | |
| 104 | genesis_data = json.loads(genesis_bytes.decode()) |
| 105 | |
| 106 | return GenesisConfiguration( |
| 107 | chain_id=U64(genesis_data["config"]["chainId"]), |
| 108 | difficulty=hex_to_uint(genesis_data["difficulty"]), |
| 109 | extra_data=hex_to_bytes(genesis_data["extraData"]), |
| 110 | gas_limit=hex_to_uint(genesis_data["gasLimit"]), |
| 111 | nonce=hex_to_bytes8(genesis_data["nonce"]), |
| 112 | timestamp=hex_or_base_10_str_to_u256(genesis_data["timestamp"]), |
| 113 | initial_accounts=genesis_data["alloc"], |
| 114 | ) |
hex_or_base_10_str_to_u256
Convert a string in either hexadecimal or base-10 to a U256.
def hex_or_base_10_str_to_u256(balance: str) -> U256:
| 118 | """ |
|---|---|
| 119 | Convert a string in either hexadecimal or base-10 to a `U256`. |
| 120 | """ |
| 121 | if balance.startswith("0x"): |
| 122 | return hex_to_u256(balance) |
| 123 | else: |
| 124 | return U256(int(balance)) |
AddressT
| 127 | AddressT = TypeVar("AddressT", bound=FixedBytes) |
|---|
AccountT
| 128 | AccountT = TypeVar("AccountT") |
|---|
StateT
| 129 | StateT = TypeVar("StateT") |
|---|
TrieT
| 130 | TrieT = TypeVar("TrieT") |
|---|
BloomT
| 131 | BloomT = TypeVar("BloomT") |
|---|
HeaderT
| 132 | HeaderT = TypeVar("HeaderT") |
|---|
BlockT
| 133 | BlockT = TypeVar("BlockT") |
|---|
GenesisFork
Pointers to the various types and functions required to build a genesis block.
| 136 | @slotted_freezable |
|---|
| 137 | @dataclass |
|---|
class GenesisFork:
Address
| 146 | Address: Type[FixedBytes] |
|---|
Account
| 147 | Account: Callable[[Uint, U256, Bytes], AccountT] |
|---|
Trie
| 148 | Trie: Callable[[bool, object], TrieT] |
|---|
Bloom
| 149 | Bloom: Type[FixedBytes] |
|---|
Header
| 150 | Header: Type[HeaderT] |
|---|
Block
| 151 | Block: Type[BlockT] |
|---|
hex_to_address
| 152 | hex_to_address: Callable[[str], AddressT] |
|---|
set_account
| 153 | set_account: Callable[[StateT, AddressT, AccountT], object] |
|---|
set_storage
| 154 | set_storage: Callable[[StateT, AddressT, Bytes32, U256], object] |
|---|
state_root
| 155 | state_root: Callable[[StateT], Hash32] |
|---|
root
| 156 | root: Callable[[TrieT], object] |
|---|
add_genesis_block
Adds the genesis block to an empty blockchain.
The genesis block is an entirely sui generis block (unique) that is not governed by the general rules applying to all other Ethereum blocks. Instead, the only consensus requirement is that it must be identical to the block added by this function.
The mainnet genesis configuration was originally created using the
mk_genesis_block.py script. It is long since defunct, but is still
available at https://github.com/ethereum/genesis_block_generator.
The initial state is populated with balances based on the Ethereum presale that happened on the Bitcoin blockchain. Additional ether worth 1.98% of the presale was given to the foundation.
The state_root is set to the root of the initial state. The gas_limit
and difficulty are set to suitable starting values. In particular the
low gas limit made sending transactions impossible in the early stages of
Frontier.
The nonce field is 0x42 referencing Douglas Adams' "HitchHiker's Guide
to the Galaxy".
The extra_data field contains the hash of block 1028201 on
the pre-launch Olympus testnet. The creation of block 1028201 on Olympus
marked the "starting gun" for Ethereum block creation. Including its hash
in the genesis block ensured a fair launch of the Ethereum mining process.
The remaining fields are set to appropriate default values.
On testnets the genesis configuration usually allocates 1 wei to addresses
0x00 to 0xFF to avoid edge cases around precompiles being created or
cleared (by EIP-161).
def add_genesis_block(hardfork: GenesisFork[AddressT, AccountT, StateT, TrieT, BloomT, HeaderT, BlockT], chain: Any, genesis: GenesisConfiguration) -> None:
| 166 | """ |
|---|---|
| 167 | Adds the genesis block to an empty blockchain. |
| 168 | |
| 169 | The genesis block is an entirely sui generis block (unique) that is not |
| 170 | governed by the general rules applying to all other Ethereum blocks. |
| 171 | Instead, the only consensus requirement is that it must be identical to |
| 172 | the block added by this function. |
| 173 | |
| 174 | The mainnet genesis configuration was originally created using the |
| 175 | `mk_genesis_block.py` script. It is long since defunct, but is still |
| 176 | available at <https://github.com/ethereum/genesis_block_generator>. |
| 177 | |
| 178 | The initial state is populated with balances based on the Ethereum presale |
| 179 | that happened on the Bitcoin blockchain. Additional ether worth 1.98% of |
| 180 | the presale was given to the foundation. |
| 181 | |
| 182 | The `state_root` is set to the root of the initial state. The `gas_limit` |
| 183 | and `difficulty` are set to suitable starting values. In particular the |
| 184 | low gas limit made sending transactions impossible in the early stages of |
| 185 | Frontier. |
| 186 | |
| 187 | The `nonce` field is `0x42` referencing Douglas Adams' "HitchHiker's Guide |
| 188 | to the Galaxy". |
| 189 | |
| 190 | The `extra_data` field contains the hash of block `1028201` on |
| 191 | the pre-launch Olympus testnet. The creation of block `1028201` on Olympus |
| 192 | marked the "starting gun" for Ethereum block creation. Including its hash |
| 193 | in the genesis block ensured a fair launch of the Ethereum mining process. |
| 194 | |
| 195 | The remaining fields are set to appropriate default values. |
| 196 | |
| 197 | On testnets the genesis configuration usually allocates 1 wei to addresses |
| 198 | `0x00` to `0xFF` to avoid edge cases around precompiles being created or |
| 199 | cleared (by [EIP-161]). |
| 200 | |
| 201 | [EIP-161]: https://eips.ethereum.org/EIPS/eip-161 |
| 202 | """ |
| 203 | Address: Type[FixedBytes] = hardfork.Address # noqa N806 |
| 204 | assert issubclass(Address, FixedBytes) |
| 205 | |
| 206 | for hex_address, account in genesis.initial_accounts.items(): |
| 207 | address = hardfork.hex_to_address(hex_address) |
| 208 | hardfork.set_account( |
| 209 | chain.state, |
| 210 | address, |
| 211 | hardfork.Account( |
| 212 | Uint(int(account.get("nonce", "0"))), |
| 213 | hex_or_base_10_str_to_u256(account.get("balance", 0)), |
| 214 | hex_to_bytes(account.get("code", "0x")), |
| 215 | ), |
| 216 | ) |
| 217 | for key, value in account.get("storage", {}).items(): |
| 218 | hardfork.set_storage( |
| 219 | chain.state, address, hex_to_bytes32(key), hex_to_u256(value) |
| 220 | ) |
| 221 | |
| 222 | fields = { |
| 223 | "parent_hash": Hash32(b"\0" * 32), |
| 224 | "ommers_hash": keccak256(rlp.encode(())), |
| 225 | "coinbase": Address(b"\0" * Address.LENGTH), |
| 226 | "state_root": hardfork.state_root(chain.state), |
| 227 | "transactions_root": hardfork.root(hardfork.Trie(False, None)), |
| 228 | "receipt_root": hardfork.root(hardfork.Trie(False, None)), |
| 229 | "bloom": hardfork.Bloom(b"\0" * 256), |
| 230 | "difficulty": genesis.difficulty, |
| 231 | "number": Uint(0), |
| 232 | "gas_limit": genesis.gas_limit, |
| 233 | "gas_used": Uint(0), |
| 234 | "timestamp": genesis.timestamp, |
| 235 | "extra_data": genesis.extra_data, |
| 236 | "nonce": genesis.nonce, |
| 237 | } |
| 238 | |
| 239 | if has_field(hardfork.Header, "mix_digest"): |
| 240 | fields["mix_digest"] = Hash32(b"\0" * 32) |
| 241 | else: |
| 242 | fields["prev_randao"] = Hash32(b"\0" * 32) |
| 243 | |
| 244 | if has_field(hardfork.Header, "base_fee_per_gas"): |
| 245 | fields["base_fee_per_gas"] = Uint(10**9) |
| 246 | |
| 247 | if has_field(hardfork.Header, "withdrawals_root"): |
| 248 | fields["withdrawals_root"] = hardfork.root(hardfork.Trie(False, None)) |
| 249 | |
| 250 | if has_field(hardfork.Header, "blob_gas_used"): |
| 251 | fields["blob_gas_used"] = U64(0) |
| 252 | |
| 253 | if has_field(hardfork.Header, "excess_blob_gas"): |
| 254 | fields["excess_blob_gas"] = U64(0) |
| 255 | |
| 256 | if has_field(hardfork.Header, "parent_beacon_block_root"): |
| 257 | fields["parent_beacon_block_root"] = Hash32(b"\0" * 32) |
| 258 | |
| 259 | if has_field(hardfork.Header, "requests_hash"): |
| 260 | fields["requests_hash"] = Hash32(b"\0" * 32) |
| 261 | |
| 262 | genesis_header = hardfork.Header(**fields) |
| 263 | |
| 264 | block_fields = { |
| 265 | "header": genesis_header, |
| 266 | "transactions": (), |
| 267 | "ommers": (), |
| 268 | } |
| 269 | |
| 270 | if has_field(hardfork.Block, "withdrawals"): |
| 271 | block_fields["withdrawals"] = () |
| 272 | |
| 273 | if has_field(hardfork.Block, "requests"): |
| 274 | block_fields["requests"] = () |
| 275 | |
| 276 | genesis_block = hardfork.Block(**block_fields) |
| 277 | |
| 278 | chain.blocks.append(genesis_block) |
| 279 | chain.chain_id = genesis.chain_id |