Skip to content

Preliminary london fixups #2022

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions eth/tools/_utils/normalization.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,8 @@ def normalize_block_header(header: Dict[str, Any]) -> Dict[str, Any]:
}
if 'blocknumber' in header:
normalized_header['blocknumber'] = to_int(header['blocknumber'])
if 'baseFeePerGas' in header:
normalized_header['baseFeePerGas'] = to_int(header['baseFeePerGas'])
if 'chainname' in header:
normalized_header['chainname'] = header['chainname']
if 'chainnetwork' in header:
Expand Down
46 changes: 29 additions & 17 deletions eth/tools/fixtures/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
Type,
)

from eth_utils.toolz import first
from eth_utils.toolz import (
assoc,
first,
)

from eth_utils import (
to_normalized_address,
Expand Down Expand Up @@ -133,6 +136,10 @@ def chain_vm_configuration(fixture: Dict[str, Any]) -> Iterable[Tuple[int, Type[
return (
(0, BerlinVM),
)
elif network == 'London':
return (
(0, LondonVM),
)
elif network == 'FrontierToHomesteadAt5':
HomesteadVM = BaseHomesteadVM.configure(support_dao_fork=False)
return (
Expand Down Expand Up @@ -177,23 +184,28 @@ def genesis_fields_from_fixture(fixture: Dict[str, Any]) -> Dict[str, Any]:
Convert all genesis fields in a fixture to a dictionary of header fields and values.
"""

return {
'parent_hash': fixture['genesisBlockHeader']['parentHash'],
'uncles_hash': fixture['genesisBlockHeader']['uncleHash'],
'coinbase': fixture['genesisBlockHeader']['coinbase'],
'state_root': fixture['genesisBlockHeader']['stateRoot'],
'transaction_root': fixture['genesisBlockHeader']['transactionsTrie'],
'receipt_root': fixture['genesisBlockHeader']['receiptTrie'],
'bloom': fixture['genesisBlockHeader']['bloom'],
'difficulty': fixture['genesisBlockHeader']['difficulty'],
'block_number': fixture['genesisBlockHeader']['number'],
'gas_limit': fixture['genesisBlockHeader']['gasLimit'],
'gas_used': fixture['genesisBlockHeader']['gasUsed'],
'timestamp': fixture['genesisBlockHeader']['timestamp'],
'extra_data': fixture['genesisBlockHeader']['extraData'],
'mix_hash': fixture['genesisBlockHeader']['mixHash'],
'nonce': fixture['genesisBlockHeader']['nonce'],
header_fields = fixture['genesisBlockHeader']
base_fields = {
'parent_hash': header_fields['parentHash'],
'uncles_hash': header_fields['uncleHash'],
'coinbase': header_fields['coinbase'],
'state_root': header_fields['stateRoot'],
'transaction_root': header_fields['transactionsTrie'],
'receipt_root': header_fields['receiptTrie'],
'bloom': header_fields['bloom'],
'difficulty': header_fields['difficulty'],
'block_number': header_fields['number'],
'gas_limit': header_fields['gasLimit'],
'gas_used': header_fields['gasUsed'],
'timestamp': header_fields['timestamp'],
'extra_data': header_fields['extraData'],
'mix_hash': header_fields['mixHash'],
'nonce': header_fields['nonce'],
}
if 'baseFeePerGas' in header_fields:
return assoc(base_fields, 'base_fee_per_gas', header_fields['baseFeePerGas'])
else:
return base_fields


def genesis_params_from_fixture(fixture: Dict[str, Any]) -> Dict[str, Any]:
Expand Down
24 changes: 16 additions & 8 deletions eth/vm/forks/london/headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,25 @@ def create_header_from_parent(difficulty_fn: Callable[[BlockHeaderAPI, int], int
header_params['timestamp'],
)

# The general fill function doesn't recognize this custom field, so remove it
configured_fee_per_gas = header_params.pop('base_fee_per_gas', None)

all_fields = fill_header_params_from_parent(parent_header, **header_params)

# must add the new field *after* filling, because the general fill function doesn't recognize it
base_fee_per_gas = calculate_expected_base_fee_per_gas(parent_header)
if 'base_fee_per_gas' in header_params and all_fields['base_fee_per_gas'] != base_fee_per_gas:
raise ValidationError(
f"Cannot select an invalid base_fee_per_gas of:"
f" {all_fields['base_fee_per_gas']!r}, expected: {base_fee_per_gas}"
)
calculated_fee_per_gas = calculate_expected_base_fee_per_gas(parent_header)
if configured_fee_per_gas is None:
all_fields['base_fee_per_gas'] = calculated_fee_per_gas
else:
all_fields['base_fee_per_gas'] = base_fee_per_gas
# Must not configure an invalid base fee. So verify that either:
# 1. This is the genesis header, or
# 2. The configured value matches the calculated value from the parent
if parent_header is None or configured_fee_per_gas == calculated_fee_per_gas:
all_fields['base_fee_per_gas'] = configured_fee_per_gas
else:
raise ValidationError(
f"Cannot select an invalid base_fee_per_gas of:"
f" {configured_fee_per_gas}, expected: {calculated_fee_per_gas}"
)

new_header = LondonBlockHeader(**all_fields) # type:ignore
return new_header
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2022.internal.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
During fixture tests, verify that the generated genesis block matches the fixture's RLP-encoding.
17 changes: 13 additions & 4 deletions tests/json-fixtures/blockchain/test_blockchain.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from eth.tools.rlp import (
assert_mined_block_unchanged,
assert_headers_eq,
)
from eth.tools._utils.normalization import (
normalize_blockchain_fixtures,
Expand All @@ -24,6 +25,7 @@
should_run_slow_tests,
verify_state,
)
from eth.vm.header import HeaderSedes


ROOT_PROJECT_DIR = Path(__file__).parents[3]
Expand Down Expand Up @@ -325,15 +327,22 @@ def test_blockchain_fixtures(fixture_data, fixture):
except ValueError as e:
raise AssertionError(f"could not load chain for {fixture_data}") from e

# TODO: find out if this is supposed to pass?
# if 'genesisRLP' in fixture:
# assert rlp.encode(genesis_header) == fixture['genesisRLP']

genesis_fields = genesis_fields_from_fixture(fixture)

genesis_block = chain.get_canonical_block_by_number(0)
genesis_header = genesis_block.header

# Validate the genesis header RLP against the generated header
if 'genesisRLP' in fixture:
# Super hacky, but better than nothing: extract the header, then re-decode it
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

works well! 👍

fixture_decoded_block = rlp.decode(fixture['genesisRLP'])
fixture_encoded_header = rlp.encode(fixture_decoded_block[0])
fixture_header = rlp.decode(fixture_encoded_header, sedes=HeaderSedes)
# Error message with pretty output if header doesn't match
assert_headers_eq(fixture_header, genesis_header)
# Last gut check that transactions & receipts are valid, too
assert rlp.encode(genesis_block) == fixture['genesisRLP']

assert_imported_genesis_header_unchanged(genesis_fields, genesis_header)

# 1 - mine the genesis block
Expand Down