Skip to content
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

Enforce terminal PoW block to be on the cusp #2522

Merged
merged 3 commits into from
Jul 20, 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: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ def sundry_functions(cls) -> str:


def get_pow_block(hash: Bytes32) -> PowBlock:
return PowBlock(block_hash=hash, is_valid=True, is_processed=True,
return PowBlock(block_hash=hash, parent_hash=Bytes32(), is_valid=True, is_processed=True,
total_difficulty=uint256(0), difficulty=uint256(0))


Expand Down
9 changes: 6 additions & 3 deletions specs/merge/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class TransitionStore(object):
@dataclass
class PowBlock(object):
block_hash: Hash32
parent_hash: Hash32
is_processed: boolean
is_valid: boolean
total_difficulty: uint256
Expand All @@ -99,9 +100,10 @@ Let `get_pow_block(block_hash: Hash32) -> PowBlock` be the function that given t
Used by fork-choice handler, `on_block`.

```python
def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlock) -> bool:
def is_valid_terminal_pow_block(transition_store: TransitionStore, block: PowBlock, parent: PowBlock) -> bool:
is_total_difficulty_reached = block.total_difficulty >= transition_store.transition_total_difficulty
return block.is_valid and is_total_difficulty_reached
is_parent_total_difficulty_valid = parent.total_difficulty < transition_store.transition_total_difficulty
return block.is_valid and is_total_difficulty_reached and is_parent_total_difficulty_valid
```

## Updated fork-choice handlers
Expand Down Expand Up @@ -130,8 +132,9 @@ def on_block(store: Store, signed_block: SignedBeaconBlock, transition_store: Tr
if (transition_store is not None) and is_merge_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)
pow_parent = get_pow_block(pow_block.parent_hash)
assert pow_block.is_processed
assert is_valid_terminal_pow_block(transition_store, pow_block)
assert is_valid_terminal_pow_block(transition_store, pow_block, pow_parent)

# Check the block is valid and compute the post-state
state = pre_state.copy()
Expand Down
24 changes: 15 additions & 9 deletions specs/merge/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
- [Block proposal](#block-proposal)
- [Constructing the `BeaconBlockBody`](#constructing-the-beaconblockbody)
- [Execution Payload](#execution-payload)
- [`get_pow_chain_head`](#get_pow_chain_head)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->
Expand Down Expand Up @@ -63,13 +62,19 @@ All validator responsibilities remain unchanged other than those noted below. Na

##### Execution Payload

###### `get_pow_chain_head`
* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine, pow_chain)` where:

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.
```python
def get_pow_block_at_total_difficulty(total_difficulty: uint256, pow_chain: Sequence[PowBlock]) -> Optional[PowBlock]:
# `pow_chain` abstractly represents all blocks in the PoW chain
for block in pow_chain:
parent = get_pow_block(block.parent_hash)
if block.total_difficulty >= total_difficulty and parent.total_difficulty < total_difficulty:
return block

return None

* Set `block.body.execution_payload = get_execution_payload(state, transition_store, execution_engine)` where:

```python
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))
Expand All @@ -87,15 +92,16 @@ def produce_execution_payload(state: BeaconState,
def get_execution_payload(state: BeaconState,
transition_store: TransitionStore,
randao_reveal: BLSSignature,
execution_engine: ExecutionEngine) -> ExecutionPayload:
execution_engine: ExecutionEngine,
pow_chain: Sequence[PowBlock]) -> ExecutionPayload:
if not is_merge_complete(state):
pow_block = get_pow_chain_head()
if not is_valid_terminal_pow_block(transition_store, pow_block):
terminal_pow_block = get_pow_block_at_total_difficulty(transition_store.transition_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, pow_block.block_hash, randao_reveal, execution_engine)
return produce_execution_payload(state, terminal_pow_block.block_hash, randao_reveal, execution_engine)

# Post-merge, normal payload
parent_hash = state.latest_execution_payload_header.block_hash
Expand Down