diff --git a/configs/mainnet.yaml b/configs/mainnet.yaml index 47b02aa8d..80b8b6e7d 100644 --- a/configs/mainnet.yaml +++ b/configs/mainnet.yaml @@ -32,7 +32,7 @@ SHARDING_FORK_VERSION: 0x03000000 SHARDING_FORK_EPOCH: 18446744073709551615 # TBD, 2**32 is a placeholder. Merge transition approach is in active R&D. -TRANSITION_TOTAL_DIFFICULTY: 4294967296 +TRANSITION_TOTAL_DIFFICULTY_OFFSET: 4294967296 # Time parameters diff --git a/configs/minimal.yaml b/configs/minimal.yaml index 1a04c4ecd..8abbdb7fc 100644 --- a/configs/minimal.yaml +++ b/configs/minimal.yaml @@ -31,7 +31,7 @@ SHARDING_FORK_VERSION: 0x03000001 SHARDING_FORK_EPOCH: 18446744073709551615 # TBD, 2**32 is a placeholder. Merge transition approach is in active R&D. -TRANSITION_TOTAL_DIFFICULTY: 4294967296 +TRANSITION_TOTAL_DIFFICULTY_OFFSET: 4294967296 # Time parameters diff --git a/setup.py b/setup.py index 7466ec790..80e3d8d9f 100644 --- a/setup.py +++ b/setup.py @@ -509,7 +509,7 @@ ExecutionState = Any def get_pow_block(hash: Bytes32) -> PowBlock: return PowBlock(block_hash=hash, is_valid=True, is_processed=True, - total_difficulty=config.TRANSITION_TOTAL_DIFFICULTY) + total_difficulty=uint256(0)) def get_execution_state(execution_state_root: Bytes32) -> ExecutionState: diff --git a/specs/merge/beacon-chain.md b/specs/merge/beacon-chain.md index c3ee81d76..621ebfddc 100644 --- a/specs/merge/beacon-chain.md +++ b/specs/merge/beacon-chain.md @@ -70,7 +70,7 @@ Warning: this configuration is not definitive. | Name | Value | | - | - | -| `TRANSITION_TOTAL_DIFFICULTY` | **TBD** | +| `TRANSITION_TOTAL_DIFFICULTY_OFFSET` | **TBD** | ## Containers diff --git a/specs/merge/fork-choice.md b/specs/merge/fork-choice.md index 9e6c341bc..59dc8bad2 100644 --- a/specs/merge/fork-choice.md +++ b/specs/merge/fork-choice.md @@ -12,13 +12,14 @@ - [`ExecutionEngine`](#executionengine) - [`set_head`](#set_head) - [`finalize_block`](#finalize_block) -- [Containers](#containers) - - [`PowBlock`](#powblock) -- [Helper functions](#helper-functions) - - [`get_pow_block`](#get_pow_block) - - [`is_valid_transition_block`](#is_valid_transition_block) +- [Helpers](#helpers) + - [`TransitionStore`](#transitionstore) + - [`PowBlock`](#powblock) + - [`get_pow_block`](#get_pow_block) + - [`get_transition_store`](#get_transition_store) + - [`is_valid_transition_block`](#is_valid_transition_block) - [Updated fork-choice handlers](#updated-fork-choice-handlers) - - [`on_block`](#on_block) + - [`on_block`](#on_block) @@ -66,44 +67,59 @@ def finalize_block(self: ExecutionEngine, block_hash: Hash32) -> bool: ... ``` -## Containers +## Helpers -#### `PowBlock` +### `TransitionStore` ```python -class PowBlock(Container): +@dataclass +class TransitionStore(object): + transition_total_difficulty: uint256 +``` + +### `PowBlock` + +```python +@dataclass +class PowBlock(object): block_hash: Hash32 is_processed: boolean is_valid: boolean total_difficulty: uint256 ``` -## Helper functions - -#### `get_pow_block` +### `get_pow_block` 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. -#### `is_valid_transition_block` +### `get_transition_store` + +```python +def get_transition_store(anchor_pow_block: PowBlock): + transition_total_difficulty = pow_block.total_difficulty + TRANSITION_TOTAL_DIFFICULTY_OFFSET + return TransitionStore(transition_total_difficulty=transition_total_difficulty) +``` + +### `is_valid_transition_block` Used by fork-choice handler, `on_block`. ```python -def is_valid_transition_block(block: PowBlock) -> bool: - is_total_difficulty_reached = block.total_difficulty >= TRANSITION_TOTAL_DIFFICULTY +def is_valid_transition_block(transition_store: TransitionStore, block: PowBlock) -> bool: + is_total_difficulty_reached = block.total_difficulty >= transition_store.transition_total_difficulty return block.is_valid and is_total_difficulty_reached ``` ## Updated fork-choice handlers -#### `on_block` +### `on_block` *Note*: The only modification is the addition of the verification of transition block conditions. ```python -def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: +def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: TransitionStore=None) -> None: block = signed_block.message # Parent block must be known assert block.parent_root in store.block_states @@ -119,11 +135,12 @@ def on_block(store: Store, signed_block: SignedBeaconBlock) -> None: assert get_ancestor(store, block.parent_root, finalized_slot) == store.finalized_checkpoint.root # [New in Merge] - if is_transition_block(pre_state, block): + is_transition_store_initialized = transition_store is not None + if is_transition_store_initialized and is_transition_block(pre_state, block): # Delay consideration of block until PoW block is processed by the PoW node pow_block = get_pow_block(block.body.execution_payload.parent_hash) assert pow_block.is_processed - assert is_valid_transition_block(pow_block) + assert is_valid_transition_block(transition_store, pow_block) # Check the block is valid and compute the post-state state = pre_state.copy() diff --git a/specs/merge/fork.md b/specs/merge/fork.md index ad6e3ad3b..373b57cdc 100644 --- a/specs/merge/fork.md +++ b/specs/merge/fork.md @@ -12,6 +12,7 @@ - [Fork to Merge](#fork-to-merge) - [Fork trigger](#fork-trigger) - [Upgrading the state](#upgrading-the-state) + - [Initializing transition store](#initializing-transition-store) @@ -87,3 +88,15 @@ def upgrade_to_merge(pre: phase0.BeaconState) -> BeaconState: return post ``` + +### Initializing transition store + +If `state.slot % SLOTS_PER_EPOCH == 0` and `compute_epoch_at_slot(state.slot) == MERGE_FORK_EPOCH`, 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 initialize_transition_store(state: BeaconState) -> TransitionStore: + pow_block = get_pow_block(state.eth1_data.block_hash) + return get_transition_store(pow_block) +``` diff --git a/specs/merge/validator.md b/specs/merge/validator.md index c4c396059..5b82030e0 100644 --- a/specs/merge/validator.md +++ b/specs/merge/validator.md @@ -20,7 +20,6 @@ - [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody) - [Execution Payload](#execution-payload) - [`get_pow_chain_head`](#get_pow_chain_head) - - [`produce_execution_payload`](#produce_execution_payload) @@ -68,18 +67,13 @@ All validator responsibilities remain unchanged other than those noted below. Na Let `get_pow_chain_head() -> PowBlock` be the function that returns the head of the PoW chain. The body of the function is implementation specific. -###### `produce_execution_payload` - -Let `produce_execution_payload(parent_hash: Hash32, timestamp: uint64) -> ExecutionPayload` be the function that produces new instance of execution payload. -The `ExecutionEngine` protocol is used for the implementation specific part of execution payload proposals. - -* Set `block.body.execution_payload = get_execution_payload(state)` where: +* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine)` where: ```python -def get_execution_payload(state: BeaconState, execution_engine: ExecutionEngine) -> ExecutionPayload: +def get_execution_payload(state: BeaconState, transition_store: TransitionStore, execution_engine: ExecutionEngine) -> ExecutionPayload: if not is_transition_completed(state): pow_block = get_pow_chain_head() - if not is_valid_transition_block(pow_block): + if not is_valid_transition_block(transition_store, pow_block): # Pre-merge, empty payload return ExecutionPayload() else: