diff --git a/fork_choice/safe-block.md b/fork_choice/safe-block.md new file mode 100644 index 000000000..490d24538 --- /dev/null +++ b/fork_choice/safe-block.md @@ -0,0 +1,48 @@ +# Fork Choice -- Safe Block + +## Table of contents + + + + +- [Introduction](#introduction) +- [`get_safe_beacon_block_root`](#get_safe_beacon_block_root) +- [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash) + + + + +## 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). diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index 488f8bac5..60a54da9c 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -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) @@ -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 diff --git a/specs/bellatrix/validator.md b/specs/bellatrix/validator.md index dc64d1c8e..94671db08 100644 --- a/specs/bellatrix/validator.md +++ b/specs/bellatrix/validator.md @@ -110,9 +110,10 @@ 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 @@ -120,6 +121,7 @@ To obtain an execution payload, a block proposer building a block on top of a `s ```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]: @@ -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, ) diff --git a/tests/core/pyspec/eth2spec/test/bellatrix/unittests/validator/test_validator.py b/tests/core/pyspec/eth2spec/test/bellatrix/unittests/validator/test_validator.py index 770c05d94..4474ef853 100644 --- a/tests/core/pyspec/eth2spec/test/bellatrix/unittests/validator/test_validator.py +++ b/tests/core/pyspec/eth2spec/test/bellatrix/unittests/validator/test_validator.py @@ -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 @@ -158,6 +159,7 @@ def test_prepare_execution_payload(spec, state): 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(), )