From 199398c503a37ee9f7d9bb6e4ec7252233d16e4b Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 22 Mar 2022 20:26:09 +0600 Subject: [PATCH 1/9] Bellatrix: pass justified as a safe block --- specs/bellatrix/fork-choice.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index 488f8bac5..612e840c3 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -72,7 +72,23 @@ 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 in a call to `notify_forkchoice_updated` function +MUST be set to the return value of the following function: + +```python +def get_safe_block_hash(store: Store) -> Hash32: + # Use most recent justified block as a stopgap + safe_block_root = store.justified_checkpoint.root + 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() +``` ## Helpers From d195e066adb041224e99a8ed6160ea03f9de97f4 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 22 Mar 2022 20:41:42 +0600 Subject: [PATCH 2/9] Fix toc --- specs/bellatrix/fork-choice.md | 1 + 1 file changed, 1 insertion(+) diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index 612e840c3..b03c0f4e4 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) From 95a232780079d2fe3e026295a4e28c3247ba8fc2 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 22 Mar 2022 22:51:45 +0600 Subject: [PATCH 3/9] Bellatrix: add get_safe_block_hash to validator.md --- specs/bellatrix/validator.md | 7 ++++--- .../test/bellatrix/unittests/validator/test_validator.py | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/specs/bellatrix/validator.md b/specs/bellatrix/validator.md index dc64d1c8e..c19b07caf 100644 --- a/specs/bellatrix/validator.md +++ b/specs/bellatrix/validator.md @@ -110,10 +110,11 @@ 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 * `finalized_block_hash` is the hash of the latest finalized execution payload (`Hash32()` if none yet finalized) + * `safe_block_hash` is the return value of the `get_safe_block_hash(store: Store)` function call * `suggested_fee_recipient` is the value suggested to be used for the `fee_recipient` field of the execution payload @@ -121,6 +122,7 @@ To obtain an execution payload, a block proposer building a block on top of a `s def prepare_execution_payload(state: BeaconState, pow_chain: Dict[Hash32, PowBlock], finalized_block_hash: Hash32, + safe_block_hash: Hash32, suggested_fee_recipient: ExecutionAddress, execution_engine: ExecutionEngine) -> Optional[PayloadId]: if not is_merge_transition_complete(state): @@ -149,8 +151,7 @@ def prepare_execution_payload(state: BeaconState, # 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(), ) From 046eaf2ea014577f5b4c8413c30548eb72f6ea46 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Tue, 22 Mar 2022 22:53:47 +0600 Subject: [PATCH 4/9] Bellatrix: remove a comment about safe head stub --- specs/bellatrix/validator.md | 1 - 1 file changed, 1 deletion(-) diff --git a/specs/bellatrix/validator.md b/specs/bellatrix/validator.md index c19b07caf..458546e76 100644 --- a/specs/bellatrix/validator.md +++ b/specs/bellatrix/validator.md @@ -148,7 +148,6 @@ 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, safe_block_hash=safe_block_hash, From bc95973232666c84d1bff828cdd3c107b91e2cbe Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 24 Mar 2022 16:35:28 +0600 Subject: [PATCH 5/9] Reorder params Co-authored-by: Hsiao-Wei Wang --- specs/bellatrix/validator.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/bellatrix/validator.md b/specs/bellatrix/validator.md index 458546e76..ee19f3d07 100644 --- a/specs/bellatrix/validator.md +++ b/specs/bellatrix/validator.md @@ -113,16 +113,16 @@ To obtain an execution payload, a block proposer building a block on top of a `s 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 - * `finalized_block_hash` is the hash of the latest finalized execution payload (`Hash32()` if none yet finalized) * `safe_block_hash` is the return value of the `get_safe_block_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], - finalized_block_hash: Hash32, safe_block_hash: Hash32, + finalized_block_hash: Hash32, suggested_fee_recipient: ExecutionAddress, execution_engine: ExecutionEngine) -> Optional[PayloadId]: if not is_merge_transition_complete(state): From c97cc6f4dc8c3a25c7727b5aba859df00e6c4c9c Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Thu, 24 Mar 2022 17:22:11 +0600 Subject: [PATCH 6/9] Add separate get_safe_beacon_block function --- fork_choice/safe-block.md | 31 +++++++++++++++++++++++++++++++ specs/bellatrix/fork-choice.md | 3 +-- 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 fork_choice/safe-block.md diff --git a/fork_choice/safe-block.md b/fork_choice/safe-block.md new file mode 100644 index 000000000..158a2b069 --- /dev/null +++ b/fork_choice/safe-block.md @@ -0,0 +1,31 @@ +# Fork Choice -- Safe Block + +## Table of contents + + + + +- [Introduction](#introduction) +- [`get_safe_beacon_block`](#get_safe_beacon_block) + + + + +## 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` + +```python +def get_safe_beacon_block(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. diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index b03c0f4e4..05bde7e65 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -80,8 +80,7 @@ MUST be set to the return value of the following function: ```python def get_safe_block_hash(store: Store) -> Hash32: - # Use most recent justified block as a stopgap - safe_block_root = store.justified_checkpoint.root + safe_block_root = get_safe_beacon_block(store) safe_block = store.blocks[safe_block_root] # Return Hash32() if no payload is yet justified From bd66114f4ac464c52abeaefb3e5375156e5f55ad Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Sat, 26 Mar 2022 02:02:38 +0600 Subject: [PATCH 7/9] Clarify names and move get_safe_block_hash to safe-block.md --- fork_choice/safe-block.md | 21 ++++++++++++++++++--- specs/bellatrix/fork-choice.md | 15 +-------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/fork_choice/safe-block.md b/fork_choice/safe-block.md index 158a2b069..127002b15 100644 --- a/fork_choice/safe-block.md +++ b/fork_choice/safe-block.md @@ -6,7 +6,8 @@ - [Introduction](#introduction) -- [`get_safe_beacon_block`](#get_safe_beacon_block) +- [`get_safe_beacon_block_root`](#get_safe_beacon_block_root) +- [`get_safe_execution_payload_hash`](#get_safe_execution_payload_hash) @@ -20,12 +21,26 @@ to expose a safe block to users. This section describes an algorithm to find a safe block. -## `get_safe_beacon_block` +## `get_safe_beacon_block_root` ```python -def get_safe_beacon_block(store: Store) -> Root: +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() +``` diff --git a/specs/bellatrix/fork-choice.md b/specs/bellatrix/fork-choice.md index 05bde7e65..305d58a57 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -75,20 +75,7 @@ As per EIP-3675, before a post-transition block is finalized, `notify_forkchoice ##### `safe_block_hash` -The `safe_block_hash` parameter in a call to `notify_forkchoice_updated` function -MUST be set to the return value of the following function: - -```python -def get_safe_block_hash(store: Store) -> Hash32: - safe_block_root = get_safe_beacon_block(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() -``` +The `safe_block_hash` parameter MUST be set to return value of `get_safe_execution_payload_hash(store: Store)` function. ## Helpers From b13a9f0f8af77cedb487ba3a15ccb89b02d2b1b2 Mon Sep 17 00:00:00 2001 From: Mikhail Kalinin Date: Wed, 30 Mar 2022 15:36:01 +0600 Subject: [PATCH 8/9] Apply suggestions as per review --- fork_choice/safe-block.md | 2 ++ specs/bellatrix/fork-choice.md | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fork_choice/safe-block.md b/fork_choice/safe-block.md index 127002b15..490d24538 100644 --- a/fork_choice/safe-block.md +++ b/fork_choice/safe-block.md @@ -44,3 +44,5 @@ def get_safe_execution_payload_hash(store: Store) -> Hash32: 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 305d58a57..60a54da9c 100644 --- a/specs/bellatrix/fork-choice.md +++ b/specs/bellatrix/fork-choice.md @@ -75,7 +75,8 @@ As per EIP-3675, before a post-transition block is finalized, `notify_forkchoice ##### `safe_block_hash` -The `safe_block_hash` parameter MUST be set to return value of `get_safe_execution_payload_hash(store: Store)` function. +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 From c87fc480531655bca0369dff222ca7624cef46c2 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Wed, 30 Mar 2022 12:04:23 -0600 Subject: [PATCH 9/9] minor typo --- specs/bellatrix/validator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/bellatrix/validator.md b/specs/bellatrix/validator.md index ee19f3d07..94671db08 100644 --- a/specs/bellatrix/validator.md +++ b/specs/bellatrix/validator.md @@ -113,7 +113,7 @@ To obtain an execution payload, a block proposer building a block on top of a `s 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_block_hash(store: Store)` function call + * `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