Skip to content

Commit

Permalink
Add support for ParisVM / the merge
Browse files Browse the repository at this point in the history
- Pass through `mix_hash` value for blocks; generate a pseudorandom mixhash value for post-merge VMs.
- Add support for `safe` and `finalized` block identifiers.
- Change `miner` to `coinbase` so as to not mislead since default is post-merge.
- Change some of the docs to reflect paris fork, post-merge, default values.
- Add some relevant tests around expected values across PoW -> PoS boundary.
  • Loading branch information
fselmo committed Nov 17, 2022
1 parent 12e8f24 commit 830ca91
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 68 deletions.
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ Any `block_number` parameter will accept the following string values.
* `'latest'`: for the latest mined block.
* `'pending'`: for the current un-mined block.
* `'earliest'`: for the genesis block.
* `'safe'`: for the last block that has passed 2/3 of attestations post-merge.
* `'finalized'`: for the last finalized block post-merge.

> Note: These **must** be text strings (not byte stringS)
Expand Down Expand Up @@ -221,7 +223,7 @@ object accepts the following parameters.
### Fork Rules
<a id="fork-rules"></a>

Ethereum tester uses the London fork rules, starting at block 0.
Ethereum tester uses the Paris (PoS) fork rules, starting at block 0.

### Time Travel
<a id="time-travel"></a>
Expand All @@ -245,14 +247,14 @@ parameter of these methods **must** be a hexadecimal encoded address.

<a id="api-mine_blocks"></a>

#### `EthereumTester.mine_blocks(num_blocks=1, coinbase=None)`
#### `EthereumTester.mine_blocks(num_blocks=1, coinbase=ZERO_ADDRESS)`

Mines `num_blocks` new blocks, returning an iterable of the newly mined block hashes.


<a id="api-mine_block"></a>

#### `EthereumTester.mine_block(coinbase=None)`
#### `EthereumTester.mine_block(coinbase=ZERO_ADDRESS)`

Mines a single new block, returning the mined block's hash.

Expand Down Expand Up @@ -444,15 +446,16 @@ cannot be found.
{'number': 1,
'hash': '0xd481955268d1f3db58ee61685a899a35e33e8fd35b9cc0812f85b9f06757140e',
'parent_hash': '0x5be984ab842071903ee443a5dee92603bef42de35b4e10928e753f7e88a7163a',
'nonce': '0x0000000000000042',
'nonce': '0x0000000000000000',
'sha3_uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
'logs_bloom': 0,
'transactions_root': '0xef1e11d99f7db22fd93c6a10d44753d4a93e9f6ecb2f1e5030a0a91f1d3b07ac',
'receipts_root': '0x611e48488cf80b4c31f01ad45b6ebea533a68255a6d0240d434d9366a3582010',
'state_root': '0x9ce568dcaa6f130d733b333304f2c26a19334ed328a7eb9bb31707306381ba65',
'miner': '0x0000000000000000000000000000000000000000',
'difficulty': 131072,
'total_difficulty': 131072,
'coinbase': '0x0000000000000000000000000000000000000000',
'difficulty': 0,
'total_difficulty': 0,
'mix_hash': '0x0000000000000000000000000000000000000000000000000000000000000000',
'size': 751,
'extra_data': '0x0000000000000000000000000000000000000000000000000000000000000000',
'gas_limit': 3141592,
Expand Down Expand Up @@ -480,15 +483,16 @@ cannot be found.
{'number': 1,
'hash': '0xd481955268d1f3db58ee61685a899a35e33e8fd35b9cc0812f85b9f06757140e',
'parent_hash': '0x5be984ab842071903ee443a5dee92603bef42de35b4e10928e753f7e88a7163a',
'nonce': '0x0000000000000042',
'nonce': '0x0000000000000000',
'sha3_uncles': '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
'logs_bloom': 0,
'transactions_root': '0xef1e11d99f7db22fd93c6a10d44753d4a93e9f6ecb2f1e5030a0a91f1d3b07ac',
'receipts_root': '0x611e48488cf80b4c31f01ad45b6ebea533a68255a6d0240d434d9366a3582010',
'state_root': '0x9ce568dcaa6f130d733b333304f2c26a19334ed328a7eb9bb31707306381ba65',
'miner': '0x0000000000000000000000000000000000000000',
'difficulty': 131072,
'total_difficulty': 131072,
'coinbase': '0x0000000000000000000000000000000000000000',
'difficulty': 0,
'total_difficulty': 0,
'mix_hash': '0x0000000000000000000000000000000000000000000000000000000000000000',
'size': 751,
'extra_data': '0x0000000000000000000000000000000000000000000000000000000000000000',
'gas_limit': 3141592,
Expand Down Expand Up @@ -559,7 +563,7 @@ gain a discount on the gas for those executions (see quickstart example for usag

#### Dynamic fee transactions (EIP-1559)
* `max_fee_per_gas`: Sets the maximum fee per unit of gas in wei that will be paid for transaction execution (integer).
* `max_priority_fee_per_gas`: Sets the fee per unit of gas in wei that is sent to the miner as an incentive for mining the transaction (integer).
* `max_priority_fee_per_gas`: Sets the fee per unit of gas in wei that is sent to the coinbase address as an incentive for including the transaction (integer).
* `access_list` (optional): Specifies accounts and storage slots expected to be accessed, based on the transaction, in order to
gain a discount on the gas for those executions (see quickstart example for usage).

Expand Down
4 changes: 3 additions & 1 deletion eth_tester/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
abstractmethod,
)

ZERO_ADDRESS = 20 * b"\x00"


class BaseChainBackend(metaclass=ABCMeta):
#
Expand Down Expand Up @@ -31,7 +33,7 @@ def time_travel(self, to_timestamp):
# Mining
#
@abstractmethod
def mine_blocks(self, num_blocks=1, coinbase=None):
def mine_blocks(self, num_blocks=1, coinbase=ZERO_ADDRESS):
raise NotImplementedError("Must be implemented by subclasses")

#
Expand Down
26 changes: 19 additions & 7 deletions eth_tester/backends/mock/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
from eth_tester.utils.transactions import (
extract_transaction_type,
)
from eth_typing import (
Hash32,
)
from eth_utils import (
apply_to_return_value,
is_bytes,
Expand Down Expand Up @@ -243,21 +246,25 @@ def make_receipt(transaction, block, _transaction_index, overrides=None):
GENESIS_NONCE = b"\x00\x00\x00\x00\x00\x00\x00*" # 42 encoded as big-endian-integer
BLANK_ROOT_HASH = b"V\xe8\x1f\x17\x1b\xccU\xa6\xff\x83E\xe6\x92\xc0\xf8n\x5bH\xe0\x1b\x99l\xad\xc0\x01b/\xb5\xe3c\xb4!" # noqa: E501
EMPTY_UNCLE_HASH = b"\x1d\xccM\xe8\xde\xc7]z\xab\x85\xb5g\xb6\xcc\xd4\x1a\xd3\x12E\x1b\x94\x8at\x13\xf0\xa1B\xfd@\xd4\x93G" # noqa: E501
POST_MERGE_DIFFICULTY = 0
POST_MERGE_MIX_HASH = Hash32(32 * b"\x00")
POST_MERGE_NONCE = b"\x00\x00\x00\x00\x00\x00\x00\x00"


def make_genesis_block(overrides=None):
default_genesis_block = {
"number": 0,
"hash": ZERO_32BYTES,
"parent_hash": ZERO_32BYTES,
"nonce": GENESIS_NONCE,
"nonce": POST_MERGE_NONCE,
"sha3_uncles": EMPTY_UNCLE_HASH,
"logs_bloom": 0,
"transactions_root": BLANK_ROOT_HASH,
"receipts_root": BLANK_ROOT_HASH,
"state_root": BLANK_ROOT_HASH,
"miner": ZERO_ADDRESS,
"difficulty": 131072,
"coinbase": ZERO_ADDRESS,
"difficulty": POST_MERGE_DIFFICULTY,
"mix_hash": POST_MERGE_MIX_HASH,
"total_difficulty": 131072,
"size": 0,
"extra_data": ZERO_32BYTES,
Expand Down Expand Up @@ -330,17 +337,22 @@ def make_block_from_parent(parent_block, overrides=None):
else:
yield "state_root", BLANK_ROOT_HASH

if "miner" in overrides:
yield "miner", overrides["miner"]
if "coinbase" in overrides:
yield "coinbase", overrides["coinbase"]
else:
yield "miner", ZERO_ADDRESS
yield "coinbase", ZERO_ADDRESS

if "difficulty" in overrides:
difficulty = overrides["difficulty"]
else:
difficulty = 131072
difficulty = POST_MERGE_DIFFICULTY
yield "difficulty", difficulty

if "mix_hash" in overrides:
yield "mix_hash", overrides["mix_hash"]
else:
yield "mix_hash", POST_MERGE_MIX_HASH

if "total_difficulty" in overrides:
yield "total_difficulty", overrides["total_difficulty"]
else:
Expand Down
7 changes: 5 additions & 2 deletions eth_tester/backends/mock/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import itertools
import copy
import os

from eth_utils import (
decode_hex,
Expand Down Expand Up @@ -46,6 +47,7 @@
serialize_transaction_as_hash,
serialize_receipt,
)
from ..pyevm.main import ZERO_ADDRESS


def _generate_dummy_address(idx):
Expand Down Expand Up @@ -134,7 +136,7 @@ def time_travel(self, timestamp):
# Mining
#
@to_tuple
def mine_blocks(self, num_blocks=1, coinbase=None):
def mine_blocks(self, num_blocks=1, coinbase=ZERO_ADDRESS):
for _ in range(num_blocks):
block_to_mine = dissoc(self.block, "hash")
block_hash = fake_rlp_hash(block_to_mine)
Expand All @@ -147,6 +149,7 @@ def mine_blocks(self, num_blocks=1, coinbase=None):
assign_block_info(transaction)
for transaction in mined_block["transactions"]
)
mined_block["mix_hash"] = os.urandom(32)
self.blocks.append(mined_block)
self.block = make_block_from_parent(mined_block)
yield block_hash
Expand All @@ -172,7 +175,7 @@ def get_block_by_number(self, block_number, full_transactions=False):

if block_number == self.block["number"]:
block = self.block
elif block_number == "latest":
elif block_number in ("latest", "safe", "finalized"):
try:
block = self.blocks[-1]
except IndexError:
Expand Down
69 changes: 46 additions & 23 deletions eth_tester/backends/pyevm/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import absolute_import

import os
import time

from eth_abi import abi
Expand Down Expand Up @@ -128,16 +129,24 @@ def generate_genesis_state_for_keys(account_keys, overrides=None):


def get_default_genesis_params(overrides=None):
# commented out params became un-configurable in py-evm during London refactor
# Commented out params became un-configurable in py-evm during London refactor.
# Post-merge now. Set genesis params to expect post-merge validation. If PoS field
# value defaults are not desired, use the overrides option.
from eth.constants import (
POST_MERGE_DIFFICULTY,
POST_MERGE_MIX_HASH,
POST_MERGE_NONCE,
)

default_genesis_params = {
# "bloom": 0,
"coinbase": GENESIS_COINBASE,
"difficulty": GENESIS_DIFFICULTY,
"difficulty": POST_MERGE_DIFFICULTY,
"extra_data": GENESIS_EXTRA_DATA,
"gas_limit": GENESIS_GAS_LIMIT,
# "gas_used": 0,
"mix_hash": GENESIS_MIX_HASH,
"nonce": GENESIS_NONCE,
"mix_hash": POST_MERGE_MIX_HASH,
"nonce": POST_MERGE_NONCE,
# "block_number": GENESIS_BLOCK_NUMBER,
# "parent_hash": GENESIS_PARENT_HASH,
"receipt_root": BLANK_ROOT_HASH,
Expand All @@ -160,26 +169,32 @@ def setup_tester_chain(
num_accounts=None,
vm_configuration=None,
mnemonic=None,
genesis_is_post_merge=True,
):

from eth.chains.base import MiningChain
from eth.consensus import (
NoProofConsensus,
ConsensusApplier,
)
from eth.vm.forks import ParisVM
from eth.db import get_db_backend

if vm_configuration is None:
from eth.vm.forks import LondonVM

no_proof_vms = ((0, LondonVM.configure(consensus_class=NoProofConsensus)),)
vm_config = ((0, ParisVM),)
else:
if len(vm_configuration) > 0:
_genesis_block_num, genesis_vm = vm_configuration[0]
if not issubclass(genesis_vm, ParisVM):
genesis_is_post_merge = False
consensus_applier = ConsensusApplier(NoProofConsensus)
no_proof_vms = consensus_applier.amend_vm_configuration(vm_configuration)
vm_config = consensus_applier.amend_vm_configuration(vm_configuration)

class MainnetTesterNoProofChain(MiningChain):
class MainnetTesterPosChain(MiningChain):
# TODO: Once the logic within `MiningChain` is refactored more generally in
# py-evm, change this class inheritance to reflect that since a `PosConsensus`
# chain does not mine.
chain_id = 131277322940537
vm_configuration = no_proof_vms
vm_configuration = vm_config

def create_header_from_parent(self, parent_header, **header_params):
# Keep the gas limit constant
Expand All @@ -192,7 +207,12 @@ def get_transaction_builder(self):
return super().get_vm().get_transaction_builder()

if genesis_params is None:
genesis_params = get_default_genesis_params()
overrides = {}
if not genesis_is_post_merge:
overrides["difficulty"] = GENESIS_DIFFICULTY
overrides["nonce"] = GENESIS_NONCE
overrides["mix_hash"] = GENESIS_MIX_HASH
genesis_params = get_default_genesis_params(overrides=overrides)

if genesis_state:
num_accounts = len(genesis_state)
Expand All @@ -207,14 +227,12 @@ def get_transaction_builder(self):

base_db = get_db_backend()

chain = MainnetTesterNoProofChain.from_genesis(
base_db, genesis_params, genesis_state
)
chain = MainnetTesterPosChain.from_genesis(base_db, genesis_params, genesis_state)
return account_keys, chain


def _get_block_by_number(chain, block_number):
if block_number == "latest":
if block_number in ("latest", "safe", "finalized"):
head_block = chain.get_block()
return chain.get_canonical_block_by_number(max(0, head_block.number - 1))
elif block_number == "earliest":
Expand Down Expand Up @@ -419,15 +437,20 @@ def time_travel(self, to_timestamp):
return to_timestamp

#
# Mining
# Importing blocks
#
@to_tuple
def mine_blocks(self, num_blocks=1, coinbase=None):
if coinbase is not None:
mine_kwargs = {"coinbase": coinbase}
else:
mine_kwargs = {}
def mine_blocks(self, num_blocks=1, coinbase=ZERO_ADDRESS):
from eth.vm.forks.paris import ParisVM

mine_kwargs = {"coinbase": coinbase}

for _ in range(num_blocks):
if isinstance(self.chain.get_vm(), ParisVM):
# post-merge, generate a random `mix_hash` to simulate the
# `prevrandao` value.
mine_kwargs["mix_hash"] = os.urandom(32)

block = self.chain.mine_block(**mine_kwargs)
yield block.hash

Expand Down Expand Up @@ -651,7 +674,7 @@ def estimate_gas(self, transaction, block_number="latest"):
evm_transaction, from_=transaction["from"]
)

if block_number == "latest":
if block_number in ("latest", "safe", "finalized"):
return self.chain.estimate_gas(spoofed_transaction)
elif block_number == "earliest":
return self.chain.estimate_gas(
Expand Down
3 changes: 2 additions & 1 deletion eth_tester/backends/pyevm/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ def serialize_block(block, full_transaction, is_pending):
"transactions_root": block.header.transaction_root,
"receipts_root": block.header.receipt_root,
"state_root": block.header.state_root,
"miner": block.header.coinbase,
"coinbase": block.header.coinbase,
"difficulty": block.header.difficulty,
"total_difficulty": block.header.difficulty, # TODO: actual total difficulty
"mix_hash": block.header.mix_hash,
"size": len(rlp.encode(block)),
"extra_data": pad32(block.header.extra_data),
"gas_limit": block.header.gas_limit,
Expand Down
Loading

0 comments on commit 830ca91

Please sign in to comment.