Skip to content

Commit

Permalink
Merge pull request #2858 from mkalinin/justified-to-safe-block
Browse files Browse the repository at this point in the history
Bellatrix: pass justified as a safe block
  • Loading branch information
djrtwo authored Mar 30, 2022
2 parents 70aba23 + c87fc48 commit 1bfefe3
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 5 deletions.
48 changes: 48 additions & 0 deletions fork_choice/safe-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Fork Choice -- Safe Block

## Table of contents
<!-- TOC -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->

- [Introduction](#introduction)
- [`get_safe_beacon_block_root`](#get_safe_beacon_block_root)
- [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->
<!-- /TOC -->

## Introduction

Under honest majority and certain network synchronicity assumptions
there exist a block that is safe from re-orgs. Normally this block is
pretty close to the head of canonical chain which makes it valuable
to expose a safe block to users.

This section describes an algorithm to find a safe block.

## `get_safe_beacon_block_root`

```python
def get_safe_beacon_block_root(store: Store) -> Root:
# Use most recent justified block as a stopgap
return store.justified_checkpoint.root
```
*Note*: Currently safe block algorithm simply returns `store.justified_checkpoint.root`
and is meant to be improved in the future.

## `get_safe_execution_payload_hash`

```python
def get_safe_execution_payload_hash(store: Store) -> Hash32:
safe_block_root = get_safe_beacon_block_root(store)
safe_block = store.blocks[safe_block_root]

# Return Hash32() if no payload is yet justified
if compute_epoch_at_slot(safe_block.slot) >= BELLATRIX_FORK_EPOCH:
return safe_block.body.execution_payload.block_hash
else:
return Hash32()
```

*Note*: This helper uses beacon block container extended in [Bellatrix](../specs/bellatrix/beacon-chain.md).
6 changes: 5 additions & 1 deletion specs/bellatrix/fork-choice.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- [Protocols](#protocols)
- [`ExecutionEngine`](#executionengine)
- [`notify_forkchoice_updated`](#notify_forkchoice_updated)
- [`safe_block_hash`](#safe_block_hash)
- [Helpers](#helpers)
- [`PayloadAttributes`](#payloadattributes)
- [`PowBlock`](#powblock)
Expand Down Expand Up @@ -72,7 +73,10 @@ As per EIP-3675, before a post-transition block is finalized, `notify_forkchoice

*Note*: Client software MUST call this function to initiate the payload build process to produce the merge transition block; the `head_block_hash` parameter MUST be set to the hash of a terminal PoW block in this case.

*Note*: Until safe head function is implemented, `safe_block_hash` parameter MUST be stubbed with the `head_block_hash` value.
##### `safe_block_hash`

The `safe_block_hash` parameter MUST be set to return value of
[`get_safe_execution_payload_hash(store: Store)`](../../fork_choice/safe-block.md#get_safe_execution_payload_hash) function.

## Helpers

Expand Down
8 changes: 4 additions & 4 deletions specs/bellatrix/validator.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,16 +110,18 @@ All validator responsibilities remain unchanged other than those noted below. Na

To obtain an execution payload, a block proposer building a block on top of a `state` must take the following actions:

1. Set `payload_id = prepare_execution_payload(state, pow_chain, finalized_block_hash, suggested_fee_recipient, execution_engine)`, where:
1. Set `payload_id = prepare_execution_payload(state, pow_chain, finalized_block_hash, safe_block_hash, suggested_fee_recipient, execution_engine)`, where:
* `state` is the state object after applying `process_slots(state, slot)` transition to the resulting state of the parent block processing
* `pow_chain` is a `Dict[Hash32, PowBlock]` dictionary that abstractly represents all blocks in the PoW chain with block hash as the dictionary key
* `safe_block_hash` is the return value of the `get_safe_execution_payload_hash(store: Store)` function call
* `finalized_block_hash` is the hash of the latest finalized execution payload (`Hash32()` if none yet finalized)
* `suggested_fee_recipient` is the value suggested to be used for the `fee_recipient` field of the execution payload


```python
def prepare_execution_payload(state: BeaconState,
pow_chain: Dict[Hash32, PowBlock],
safe_block_hash: Hash32,
finalized_block_hash: Hash32,
suggested_fee_recipient: ExecutionAddress,
execution_engine: ExecutionEngine) -> Optional[PayloadId]:
Expand All @@ -146,11 +148,9 @@ def prepare_execution_payload(state: BeaconState,
prev_randao=get_randao_mix(state, get_current_epoch(state)),
suggested_fee_recipient=suggested_fee_recipient,
)
# Set safe and head block hashes to the same value
return execution_engine.notify_forkchoice_updated(
head_block_hash=parent_hash,
# TODO: Use `parent_hash` as a stub for now.
safe_block_hash=parent_hash,
safe_block_hash=safe_block_hash,
finalized_block_hash=finalized_block_hash,
payload_attributes=payload_attributes,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def test_prepare_execution_payload(spec, state):

# Dummy arguments
finalized_block_hash = b'\x56' * 32
safe_block_hash = b'\x58' * 32
suggested_fee_recipient = b'\x78' * 20

# Mock execution_engine
Expand All @@ -158,6 +159,7 @@ def notify_forkchoice_updated(self,
state=state,
pow_chain=pow_chain.to_dict(),
finalized_block_hash=finalized_block_hash,
safe_block_hash=safe_block_hash,
suggested_fee_recipient=suggested_fee_recipient,
execution_engine=TestEngine(),
)
Expand Down

0 comments on commit 1bfefe3

Please sign in to comment.