Merge branch 'hardcoded-ttd' into tests/merge

This commit is contained in:
Dmitrii Shmatko 2021-09-20 22:26:35 +03:00
commit 8bfa5188c1
17 changed files with 111 additions and 135 deletions

View File

@ -173,7 +173,7 @@ define run_generator
echo "generator $(1) finished"
endef
# The tests dir itself is simply build by creating the directory (recursively creating deeper directories if necessary)
# The tests dir itself is simply built by creating the directory (recursively creating deeper directories if necessary)
$(TEST_VECTOR_DIR):
$(info creating test output directory, for generators: ${GENERATOR_TARGETS})
mkdir -p $@

View File

@ -45,7 +45,7 @@ The merge is still actively in development. The exact specification has not been
* [Merge fork](specs/merge/fork.md)
* [Fork Choice changes](specs/merge/fork-choice.md)
* [Validator additions](specs/merge/validator.md)
* [Client settings](specs/merge/client_settings.md)
* [Client settings](specs/merge/client-settings.md)
### Sharding

View File

@ -3,6 +3,12 @@
# Extends the mainnet preset
PRESET_BASE: 'mainnet'
# Transition
# ---------------------------------------------------------------
# TBD, 2**256-1 is a placeholder
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935
# Genesis
# ---------------------------------------------------------------
# `2**14` (= 16,384)
@ -31,9 +37,6 @@ MERGE_FORK_EPOCH: 18446744073709551615
SHARDING_FORK_VERSION: 0x03000000
SHARDING_FORK_EPOCH: 18446744073709551615
# TBD, 2**32 is a placeholder. Merge transition approach is in active R&D.
MIN_ANCHOR_POW_BLOCK_DIFFICULTY: 4294967296
# Time parameters
# ---------------------------------------------------------------

View File

@ -3,6 +3,12 @@
# Extends the minimal preset
PRESET_BASE: 'minimal'
# Transition
# ---------------------------------------------------------------
# TBD, 2**256-1 is a placeholder
TERMINAL_TOTAL_DIFFICULTY: 115792089237316195423570985008687907853269984665640564039457584007913129639935
# Genesis
# ---------------------------------------------------------------
# [customized]
@ -30,9 +36,6 @@ MERGE_FORK_EPOCH: 18446744073709551615
SHARDING_FORK_VERSION: 0x03000001
SHARDING_FORK_EPOCH: 18446744073709551615
# TBD, 2**32 is a placeholder. Merge transition approach is in active R&D.
MIN_ANCHOR_POW_BLOCK_DIFFICULTY: 4294967296
# Time parameters
# ---------------------------------------------------------------

View File

@ -509,8 +509,7 @@ ExecutionState = Any
def get_pow_block(hash: Bytes32) -> PowBlock:
return PowBlock(block_hash=hash, parent_hash=Bytes32(), is_valid=True, is_processed=True,
total_difficulty=uint256(0), difficulty=uint256(0))
return PowBlock(block_hash=hash, parent_hash=Bytes32(), total_difficulty=uint256(0), difficulty=uint256(0))
def get_execution_state(execution_state_root: Bytes32) -> ExecutionState:

View File

@ -14,6 +14,7 @@
- [Execution](#execution)
- [Configuration](#configuration)
- [Genesis testing settings](#genesis-testing-settings)
- [Transition settings](#transition-settings)
- [Containers](#containers)
- [Extended containers](#extended-containers)
- [`BeaconBlockBody`](#beaconblockbody)
@ -64,6 +65,7 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
| `BYTES_PER_LOGS_BLOOM` | `uint64(2**8)` (= 256) |
| `GAS_LIMIT_DENOMINATOR` | `uint64(2**10)` (= 1,024) |
| `MIN_GAS_LIMIT` | `uint64(5000)` (= 5,000) |
| `MAX_EXTRA_DATA_BYTES` | `2**5` (= 32) |
## Configuration
@ -76,6 +78,12 @@ This patch adds transaction execution to the beacon chain as part of the Merge f
| `GENESIS_GAS_LIMIT` | `uint64(30000000)` (= 30,000,000) |
| `GENESIS_BASE_FEE_PER_GAS` | `Bytes32('0x00ca9a3b00000000000000000000000000000000000000000000000000000000')` (= 1,000,000,000) |
### Transition settings
| Name | Value |
| - | - |
| `TERMINAL_TOTAL_DIFFICULTY` | **TBD** |
## Containers
### Extended containers
@ -159,6 +167,7 @@ class ExecutionPayload(Container):
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: Bytes32 # base fee introduced in EIP-1559, little-endian serialized
# Extra payload fields
block_hash: Hash32 # Hash of execution block
@ -180,6 +189,7 @@ class ExecutionPayloadHeader(Container):
gas_limit: uint64
gas_used: uint64
timestamp: uint64
extra_data: ByteList[MAX_EXTRA_DATA_BYTES]
base_fee_per_gas: Bytes32
# Extra payload fields
block_hash: Hash32 # Hash of execution block
@ -246,15 +256,17 @@ The above function is accessed through the `EXECUTION_ENGINE` module which insta
### Block processing
*Note*: The call to the `process_execution_payload` must happen before the call to the `process_randao` as the former depends on the `randao_mix` computed with the reveal of the previous block.
```python
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
if is_execution_enabled(state, block.body):
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge]
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body)
process_sync_aggregate(state, block.body.sync_aggregate)
if is_execution_enabled(state, block.body):
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE) # [New in Merge]
```
### Execution payload processing
@ -284,16 +296,16 @@ def is_valid_gas_limit(payload: ExecutionPayload, parent: ExecutionPayloadHeader
#### `process_execution_payload`
*Note:* This function depends on `process_randao` function call as it retrieves the most recent randao mix from the `state`. Implementations that are considering parallel processing of execution payload with respect to beacon chain state transition function should work around this dependency.
```python
def process_execution_payload(state: BeaconState, payload: ExecutionPayload, execution_engine: ExecutionEngine) -> None:
# Verify consistency of the parent hash, block number, random, base fee per gas and gas limit
# Verify consistency of the parent hash, block number, base fee per gas and gas limit
# with respect to the previous execution payload header
if is_merge_complete(state):
assert payload.parent_hash == state.latest_execution_payload_header.block_hash
assert payload.block_number == state.latest_execution_payload_header.block_number + uint64(1)
assert payload.random == get_randao_mix(state, get_current_epoch(state))
assert is_valid_gas_limit(payload, state.latest_execution_payload_header)
# Verify random
assert payload.random == get_randao_mix(state, get_current_epoch(state))
# Verify timestamp
assert payload.timestamp == compute_timestamp_at_slot(state, state.slot)
# Verify the execution payload is valid
@ -310,6 +322,7 @@ def process_execution_payload(state: BeaconState, payload: ExecutionPayload, exe
gas_limit=payload.gas_limit,
gas_used=payload.gas_used,
timestamp=payload.timestamp,
extra_data=payload.extra_data,
base_fee_per_gas=payload.base_fee_per_gas,
block_hash=payload.block_hash,
transactions_root=hash_tree_root(payload.transactions),

View File

@ -0,0 +1,21 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [The Merge -- Client Settings](#the-merge----client-settings)
- [Override terminal total difficulty](#override-terminal-total-difficulty)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# The Merge -- Client Settings
**Notice**: This document is a work-in-progress for researchers and implementers.
This document specifies configurable settings that clients must implement for the Merge.
### Override terminal total difficulty
To coordinate manual overrides to [`TERMINAL_TOTAL_DIFFICULTY`](./beacon-chain.md#Transition-settings) parameter, clients must provide `--terminal-total-difficulty-override` as a configurable setting. The value provided by this setting must take precedence over pre-configured `TERMINAL_TOTAL_DIFFICULTY` parameter.
Except under exceptional scenarios, this setting is expected to not be used. Sufficient warning to the user about this exceptional configurable setting should be provided.

View File

@ -1,26 +0,0 @@
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [The Merge -- Client Settings](#the-merge----client-settings)
- [Override terminal total difficulty](#override-terminal-total-difficulty)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
# The Merge -- Client Settings
**Notice**: This document is a work-in-progress for researchers and implementers.
This document specifies configurable settings that clients must implement for the Merge.
### Override terminal total difficulty
To coordinate manual overrides to [`terminal_total_difficulty`](fork-choice.md#transitionstore), clients
must provide `--terminal-total-difficulty-override` as a configurable setting.
If `TransitionStore` has already [been initialized](./fork.md#initializing-transition-store), this alters the previously initialized value of
`TransitionStore.terminal_total_difficulty`, otherwise this setting initializes `TransitionStore` with the specified, bypassing `compute_terminal_total_difficulty` and the use of an `anchor_pow_block`.
`terminal_total_difficulty`.
Except under exceptional scenarios, this setting is expected to not be used, and `terminal_total_difficulty` will operate with [default functionality](./fork.md#initializing-transition-store). Sufficient warning to the user about this exceptional configurable setting should be provided.
[here](fork.md#initializing-transition-store).

View File

@ -13,11 +13,9 @@
- [`set_head`](#set_head)
- [`finalize_block`](#finalize_block)
- [Helpers](#helpers)
- [`TransitionStore`](#transitionstore)
- [`PowBlock`](#powblock)
- [`get_pow_block`](#get_pow_block)
- [`is_valid_terminal_pow_block`](#is_valid_terminal_pow_block)
- [`process_merge_execution_payload`](#process_merge_execution_payload)
- [Updated fork-choice handlers](#updated-fork-choice-handlers)
- [`on_block`](#on_block)
@ -69,14 +67,6 @@ def finalize_block(self: ExecutionEngine, block_hash: Hash32) -> bool:
## Helpers
### `TransitionStore`
```python
@dataclass
class TransitionStore(object):
terminal_total_difficulty: uint256
```
### `PowBlock`
```python
@ -84,8 +74,6 @@ class TransitionStore(object):
class PowBlock(object):
block_hash: Hash32
parent_hash: Hash32
is_processed: boolean
is_valid: boolean
total_difficulty: uint256
difficulty: uint256
```
@ -94,30 +82,17 @@ class PowBlock(object):
Let `get_pow_block(block_hash: Hash32) -> PowBlock` be the function that given the hash of the PoW block returns its data.
*Note*: The `eth_getBlockByHash` JSON-RPC method does not distinguish invalid blocks from blocks that haven't been processed yet. Either extending this existing method or implementing a new one is required.
*Note*: The `eth_getBlockByHash` JSON-RPC method may be used to pull this information from an execution client.
### `is_valid_terminal_pow_block`
Used by fork-choice handler, `on_block`.
```python
def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlock, parent: PowBlock) -> bool:
is_total_difficulty_reached = block.total_difficulty >= transition_store.terminal_total_difficulty
is_parent_total_difficulty_valid = parent.total_difficulty < transition_store.terminal_total_difficulty
return block.is_valid and is_total_difficulty_reached and is_parent_total_difficulty_valid
```
### `process_merge_execution_payload`
Used by fork-choice handler, `on_block` to check validity of terminal block.
```python
def process_merge_execution_payload(transition_store: TransitionStore, execution_payload: ExecutionPayload) -> None:
# Delay consideration of block until PoW block is processed by the PoW node
pow_block = get_pow_block(execution_payload.parent_hash)
pow_parent = get_pow_block(pow_block.parent_hash)
assert pow_block.is_processed
assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent)
def is_valid_terminal_pow_block(block: PowBlock, parent: PowBlock) -> bool:
is_total_difficulty_reached = block.total_difficulty >= TERMINAL_TOTAL_DIFFICULTY
is_parent_total_difficulty_valid = parent.total_difficulty < TERMINAL_TOTAL_DIFFICULTY
return is_total_difficulty_reached and is_parent_total_difficulty_valid
```
## Updated fork-choice handlers
@ -127,7 +102,7 @@ def process_merge_execution_payload(transition_store: TransitionStore, execution
*Note*: The only modification is the addition of the verification of transition block conditions.
```python
def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: TransitionStore=None) -> None:
def on_block(store: Store, signed_block: SignedBeaconBlock) -> None:
block = signed_block.message
# Parent block must be known
assert block.parent_root in store.block_states
@ -142,13 +117,16 @@ def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: Tr
# Check block is a descendant of the finalized block at the checkpoint finalized slot
assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root
# [New in Merge]
if (transition_store is not None) and is_merge_block(pre_state, block.body):
process_merge_execution_payload(transition_store, block.body.execution_payload)
# Check the block is valid and compute the post-state
state = pre_state.copy()
state_transition(state, signed_block, True)
# [New in Merge]
if is_merge_block(pre_state, block.body):
pow_block = get_pow_block(block.body.execution_payload.parent_hash)
pow_parent = get_pow_block(pow_block.parent_hash)
assert is_valid_terminal_pow_block(pow_block, pow_parent)
# Add new block to the store
store.blocks[hash_tree_root(block)] = block
# Add new state for this block to the store

View File

@ -12,7 +12,6 @@
- [Fork to Merge](#fork-to-merge)
- [Fork trigger](#fork-trigger)
- [Upgrading the state](#upgrading-the-state)
- [Initializing transition store](#initializing-transition-store)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@ -28,8 +27,6 @@ Warning: this configuration is not definitive.
| - | - |
| `MERGE_FORK_VERSION` | `Version('0x02000000')` |
| `MERGE_FORK_EPOCH` | `Epoch(18446744073709551615)` **TBD** |
| `MIN_ANCHOR_POW_BLOCK_DIFFICULTY` | `uint256(2 ** 32)` **TBD** |
| `TARGET_SECONDS_TO_MERGE` | `uint64(7 * 86400)` = (604,800) |
## Fork to Merge
@ -37,8 +34,6 @@ Warning: this configuration is not definitive.
TBD. Social consensus, along with state conditions such as epoch boundary, finality, deposits, active validator count, etc. may be part of the decision process to trigger the fork. For now we assume the condition will be triggered at epoch `MERGE_FORK_EPOCH`.
Since the Merge transition process relies on `Eth1Data` in the beacon state we do want to make sure that this data is fresh. This is achieved by forcing `MERGE_FORK_EPOCH` to point to eth1 voting period boundary, i.e. `MERGE_FORK_EPOCH` should satisfy the following condition `MERGE_FORK_EPOCH % EPOCHS_PER_ETH1_VOTING_PERIOD == 0`.
Note that for the pure Merge networks, we don't apply `upgrade_to_merge` since it starts with Merge version logic.
### Upgrading the state
@ -100,33 +95,3 @@ def upgrade_to_merge(pre: altair.BeaconState) -> BeaconState:
return post
```
### Initializing transition store
If `state.slot % SLOTS_PER_EPOCH == 0`, `compute_epoch_at_slot(state.slot) == MERGE_FORK_EPOCH`, and the transition store has not already been initialized, a transition store is initialized to be further utilized by the transition process of the Merge.
Transition store initialization occurs after the state has been modified by corresponding `upgrade_to_merge` function.
```python
def compute_terminal_total_difficulty(anchor_pow_block: PowBlock) -> uint256:
seconds_per_voting_period = EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH * SECONDS_PER_SLOT
pow_blocks_per_voting_period = seconds_per_voting_period // SECONDS_PER_ETH1_BLOCK
pow_blocks_to_merge = TARGET_SECONDS_TO_MERGE // SECONDS_PER_ETH1_BLOCK
pow_blocks_after_anchor_block = ETH1_FOLLOW_DISTANCE + pow_blocks_per_voting_period + pow_blocks_to_merge
anchor_difficulty = max(MIN_ANCHOR_POW_BLOCK_DIFFICULTY, anchor_pow_block.difficulty)
return anchor_pow_block.total_difficulty + anchor_difficulty * uint256(pow_blocks_after_anchor_block)
def get_transition_store(anchor_pow_block: PowBlock) -> TransitionStore:
terminal_total_difficulty = compute_terminal_total_difficulty(anchor_pow_block)
return TransitionStore(terminal_total_difficulty=terminal_total_difficulty)
def initialize_transition_store(state: BeaconState) -> TransitionStore:
pow_block = get_pow_block(state.eth1_data.block_hash)
return get_transition_store(pow_block)
```
*Note*: Transition store can also be initialized at client startup by [overriding terminal total
difficulty](client_settings.md#override-terminal-total-difficulty).

View File

@ -62,7 +62,7 @@ All validator responsibilities remain unchanged other than those noted below. Na
##### Execution Payload
* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine, pow_chain)` where:
* Set `block.body.execution_payload = get_execution_payload(state, execution_engine, pow_chain)` where:
```python
def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]:
@ -75,35 +75,27 @@ def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequ
return None
def compute_randao_mix(state: BeaconState, randao_reveal: BLSSignature) -> Bytes32:
epoch = get_current_epoch(state)
return xor(get_randao_mix(state, epoch), hash(randao_reveal))
def produce_execution_payload(state: BeaconState,
parent_hash: Hash32,
randao_reveal: BLSSignature,
execution_engine: ExecutionEngine) -> ExecutionPayload:
timestamp = compute_timestamp_at_slot(state, state.slot)
randao_mix = compute_randao_mix(state, randao_reveal)
randao_mix = get_randao_mix(state, get_current_epoch(state))
return execution_engine.assemble_block(parent_hash, timestamp, randao_mix)
def get_execution_payload(state: BeaconState,
transition_store: TransitionStore,
randao_reveal: BLSSignature,
execution_engine: ExecutionEngine,
pow_chain: Sequence[PowBlock]) -> ExecutionPayload:
if not is_merge_complete(state):
terminal_pow_block = get_pow_block_at_total_difficulty(transition_store.terminal_total_difficulty, pow_chain)
terminal_pow_block = get_pow_block_at_total_difficulty(TERMINAL_TOTAL_DIFFICULTY, pow_chain)
if terminal_pow_block is None:
# Pre-merge, empty payload
return ExecutionPayload()
else:
# Signify merge via producing on top of the last PoW block
return produce_execution_payload(state, terminal_pow_block.block_hash, randao_reveal, execution_engine)
return produce_execution_payload(state, terminal_pow_block.block_hash, execution_engine)
# Post-merge, normal payload
parent_hash = state.latest_execution_payload_header.block_hash
return produce_execution_payload(state, parent_hash, randao_reveal, execution_engine)
return produce_execution_payload(state, parent_hash, execution_engine)
```

View File

@ -552,12 +552,12 @@ def compute_committee_index_from_shard(state: BeaconState, slot: Slot, shard: Sh
```python
def process_block(state: BeaconState, block: BeaconBlock) -> None:
process_block_header(state, block)
# is_execution_enabled is omitted, execution is enabled by default.
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE)
process_randao(state, block.body)
process_eth1_data(state, block.body)
process_operations(state, block.body) # [Modified in Sharding]
process_sync_aggregate(state, block.body.sync_aggregate)
# is_execution_enabled is omitted, execution is enabled by default.
process_execution_payload(state, block.body.execution_payload, EXECUTION_ENGINE)
```
#### Operations

View File

@ -52,7 +52,7 @@ def test_process_light_client_update_not_updated(spec, state):
sync_committee_signature = compute_aggregate_sync_committee_signature(
spec,
state,
block.slot,
block_header.slot,
committee,
)
next_sync_committee_branch = [spec.Bytes32() for _ in range(spec.floorlog2(spec.NEXT_SYNC_COMMITTEE_INDEX))]

View File

@ -100,8 +100,7 @@ def build_empty_block(spec, state, slot=None):
empty_block.body.sync_aggregate.sync_committee_signature = spec.G2_POINT_AT_INFINITY
if is_post_merge(spec):
randao_mix = spec.compute_randao_mix(state, empty_block.body.randao_reveal)
empty_block.body.execution_payload = build_empty_execution_payload(spec, state, randao_mix)
empty_block.body.execution_payload = build_empty_execution_payload(spec, state)
return empty_block

View File

@ -23,6 +23,7 @@ def build_empty_execution_payload(spec, state, randao_mix=None):
gas_limit=latest.gas_limit, # retain same limit
gas_used=0, # empty block, 0 gas
timestamp=timestamp,
extra_data=spec.ByteList[spec.MAX_EXTRA_DATA_BYTES](),
base_fee_per_gas=latest.base_fee_per_gas, # retain same base_fee
block_hash=spec.Hash32(),
transactions=empty_txs,
@ -45,6 +46,7 @@ def get_execution_payload_header(spec, execution_payload):
gas_limit=execution_payload.gas_limit,
gas_used=execution_payload.gas_used,
timestamp=execution_payload.timestamp,
extra_data=execution_payload.extra_data,
base_fee_per_gas=execution_payload.base_fee_per_gas,
block_hash=execution_payload.block_hash,
transactions_root=spec.hash_tree_root(execution_payload.transactions)

View File

@ -148,6 +148,34 @@ def test_bad_parent_hash_regular_payload(spec, state):
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_bad_random_first_payload(spec, state):
# pre-state
state = build_state_with_incomplete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.random = b'\x42' * 32
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_bad_random_regular_payload(spec, state):
# pre-state
state = build_state_with_complete_transition(spec, state)
next_slot(spec, state)
# execution payload
execution_payload = build_empty_execution_payload(spec, state)
execution_payload.random = b'\x04' * 32
yield from run_execution_payload_processing(spec, state, execution_payload, valid=False)
@with_merge_and_later
@spec_state_test
def test_bad_number_regular_payload(spec, state):

View File

@ -143,6 +143,9 @@ def process_and_sign_block_without_header_validations(spec, state, block):
state_root=spec.Bytes32(),
body_root=block.body.hash_tree_root(),
)
if is_post_merge(spec):
if spec.is_execution_enabled(state, block.body):
spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE)
# Perform rest of process_block transitions
spec.process_randao(state, block.body)
@ -150,9 +153,6 @@ def process_and_sign_block_without_header_validations(spec, state, block):
spec.process_operations(state, block.body)
if is_post_altair(spec):
spec.process_sync_aggregate(state, block.body.sync_aggregate)
if is_post_merge(spec):
if spec.is_execution_enabled(state, block.body):
spec.process_execution_payload(state, block.body.execution_payload, spec.EXECUTION_ENGINE)
# Insert post-state rot
block.state_root = state.hash_tree_root()
@ -196,8 +196,7 @@ def test_parent_from_same_slot(spec, state):
child_block.parent_root = state.latest_block_header.hash_tree_root()
if is_post_merge(spec):
randao_mix = spec.compute_randao_mix(state, child_block.body.randao_reveal)
child_block.body.execution_payload = build_empty_execution_payload(spec, state, randao_mix)
child_block.body.execution_payload = build_empty_execution_payload(spec, state)
# Show that normal path through transition fails
failed_state = state.copy()